mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-01-16 01:54:00 +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_bysrc;
|
||||||
struct hlist_head __rcu *state_byspi;
|
struct hlist_head __rcu *state_byspi;
|
||||||
struct hlist_head __rcu *state_byseq;
|
struct hlist_head __rcu *state_byseq;
|
||||||
|
struct hlist_head __percpu *state_cache_input;
|
||||||
unsigned int state_hmask;
|
unsigned int state_hmask;
|
||||||
unsigned int state_num;
|
unsigned int state_num;
|
||||||
struct work_struct state_hash_work;
|
struct work_struct state_hash_work;
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
|
|
||||||
#include <net/sock.h>
|
#include <net/sock.h>
|
||||||
#include <net/dst.h>
|
#include <net/dst.h>
|
||||||
|
#include <net/inet_dscp.h>
|
||||||
#include <net/ip.h>
|
#include <net/ip.h>
|
||||||
#include <net/route.h>
|
#include <net/route.h>
|
||||||
#include <net/ipv6.h>
|
#include <net/ipv6.h>
|
||||||
@ -184,10 +185,13 @@ struct xfrm_state {
|
|||||||
};
|
};
|
||||||
struct hlist_node byspi;
|
struct hlist_node byspi;
|
||||||
struct hlist_node byseq;
|
struct hlist_node byseq;
|
||||||
|
struct hlist_node state_cache;
|
||||||
|
struct hlist_node state_cache_input;
|
||||||
|
|
||||||
refcount_t refcnt;
|
refcount_t refcnt;
|
||||||
spinlock_t lock;
|
spinlock_t lock;
|
||||||
|
|
||||||
|
u32 pcpu_num;
|
||||||
struct xfrm_id id;
|
struct xfrm_id id;
|
||||||
struct xfrm_selector sel;
|
struct xfrm_selector sel;
|
||||||
struct xfrm_mark mark;
|
struct xfrm_mark mark;
|
||||||
@ -351,7 +355,7 @@ void xfrm_if_unregister_cb(void);
|
|||||||
|
|
||||||
struct xfrm_dst_lookup_params {
|
struct xfrm_dst_lookup_params {
|
||||||
struct net *net;
|
struct net *net;
|
||||||
int tos;
|
dscp_t dscp;
|
||||||
int oif;
|
int oif;
|
||||||
xfrm_address_t *saddr;
|
xfrm_address_t *saddr;
|
||||||
xfrm_address_t *daddr;
|
xfrm_address_t *daddr;
|
||||||
@ -536,6 +540,7 @@ struct xfrm_policy_queue {
|
|||||||
* @xp_net: network namespace the policy lives in
|
* @xp_net: network namespace the policy lives in
|
||||||
* @bydst: hlist node for SPD hash table or rbtree list
|
* @bydst: hlist node for SPD hash table or rbtree list
|
||||||
* @byidx: hlist node for index hash table
|
* @byidx: hlist node for index hash table
|
||||||
|
* @state_cache_list: hlist head for policy cached xfrm states
|
||||||
* @lock: serialize changes to policy structure members
|
* @lock: serialize changes to policy structure members
|
||||||
* @refcnt: reference count, freed once it reaches 0
|
* @refcnt: reference count, freed once it reaches 0
|
||||||
* @pos: kernel internal tie-breaker to determine age of policy
|
* @pos: kernel internal tie-breaker to determine age of policy
|
||||||
@ -566,6 +571,8 @@ struct xfrm_policy {
|
|||||||
struct hlist_node bydst;
|
struct hlist_node bydst;
|
||||||
struct hlist_node byidx;
|
struct hlist_node byidx;
|
||||||
|
|
||||||
|
struct hlist_head state_cache_list;
|
||||||
|
|
||||||
/* This lock only affects elements except for entry. */
|
/* This lock only affects elements except for entry. */
|
||||||
rwlock_t lock;
|
rwlock_t lock;
|
||||||
refcount_t refcnt;
|
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,
|
struct xfrm_state *xfrm_state_lookup(struct net *net, u32 mark,
|
||||||
const xfrm_address_t *daddr, __be32 spi,
|
const xfrm_address_t *daddr, __be32 spi,
|
||||||
u8 proto, unsigned short family);
|
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,
|
struct xfrm_state *xfrm_state_lookup_byaddr(struct net *net, u32 mark,
|
||||||
const xfrm_address_t *daddr,
|
const xfrm_address_t *daddr,
|
||||||
const xfrm_address_t *saddr,
|
const xfrm_address_t *saddr,
|
||||||
@ -1684,7 +1695,7 @@ struct xfrmk_spdinfo {
|
|||||||
u32 spdhmcnt;
|
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_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);
|
||||||
@ -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,
|
int xfrm_alloc_spi(struct xfrm_state *x, u32 minspi, u32 maxspi,
|
||||||
struct netlink_ext_ack *extack);
|
struct netlink_ext_ack *extack);
|
||||||
struct xfrm_state *xfrm_find_acq(struct net *net, const struct xfrm_mark *mark,
|
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 *daddr,
|
||||||
const xfrm_address_t *saddr, int create,
|
const xfrm_address_t *saddr, int create,
|
||||||
unsigned short family);
|
unsigned short family);
|
||||||
|
@ -322,6 +322,7 @@ enum xfrm_attr_type_t {
|
|||||||
XFRMA_MTIMER_THRESH, /* __u32 in seconds for input SA */
|
XFRMA_MTIMER_THRESH, /* __u32 in seconds for input SA */
|
||||||
XFRMA_SA_DIR, /* __u8 */
|
XFRMA_SA_DIR, /* __u8 */
|
||||||
XFRMA_NAT_KEEPALIVE_INTERVAL, /* __u32 in seconds for NAT keepalive */
|
XFRMA_NAT_KEEPALIVE_INTERVAL, /* __u32 in seconds for NAT keepalive */
|
||||||
|
XFRMA_SA_PCPU, /* __u32 */
|
||||||
__XFRMA_MAX
|
__XFRMA_MAX
|
||||||
|
|
||||||
#define XFRMA_OUTPUT_MARK XFRMA_SET_MARK /* Compatibility */
|
#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 */
|
#define XFRM_POLICY_LOCALOK 1 /* Allow user to override global policy */
|
||||||
/* Automatically expand selector to include matching ICMP payloads. */
|
/* Automatically expand selector to include matching ICMP payloads. */
|
||||||
#define XFRM_POLICY_ICMP 2
|
#define XFRM_POLICY_ICMP 2
|
||||||
|
#define XFRM_POLICY_CPU_ACQUIRE 4
|
||||||
__u8 share;
|
__u8 share;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -53,9 +53,9 @@ static struct sk_buff *esp4_gro_receive(struct list_head *head,
|
|||||||
if (sp->len == XFRM_MAX_DEPTH)
|
if (sp->len == XFRM_MAX_DEPTH)
|
||||||
goto out_reset;
|
goto out_reset;
|
||||||
|
|
||||||
x = xfrm_state_lookup(dev_net(skb->dev), skb->mark,
|
x = xfrm_input_state_lookup(dev_net(skb->dev), skb->mark,
|
||||||
(xfrm_address_t *)&ip_hdr(skb)->daddr,
|
(xfrm_address_t *)&ip_hdr(skb)->daddr,
|
||||||
spi, IPPROTO_ESP, AF_INET);
|
spi, IPPROTO_ESP, AF_INET);
|
||||||
|
|
||||||
if (unlikely(x && x->dir && x->dir != XFRM_SA_DIR_IN)) {
|
if (unlikely(x && x->dir && x->dir != XFRM_SA_DIR_IN)) {
|
||||||
/* non-offload path will record the error and audit log */
|
/* non-offload path will record the error and audit log */
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
#include <linux/inetdevice.h>
|
#include <linux/inetdevice.h>
|
||||||
#include <net/dst.h>
|
#include <net/dst.h>
|
||||||
#include <net/xfrm.h>
|
#include <net/xfrm.h>
|
||||||
|
#include <net/inet_dscp.h>
|
||||||
#include <net/ip.h>
|
#include <net/ip.h>
|
||||||
#include <net/l3mdev.h>
|
#include <net/l3mdev.h>
|
||||||
|
|
||||||
@ -24,7 +25,7 @@ static struct dst_entry *__xfrm4_dst_lookup(struct flowi4 *fl4,
|
|||||||
|
|
||||||
memset(fl4, 0, sizeof(*fl4));
|
memset(fl4, 0, sizeof(*fl4));
|
||||||
fl4->daddr = params->daddr->a4;
|
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,
|
fl4->flowi4_l3mdev = l3mdev_master_ifindex_by_index(params->net,
|
||||||
params->oif);
|
params->oif);
|
||||||
fl4->flowi4_mark = params->mark;
|
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)
|
if (sp->len == XFRM_MAX_DEPTH)
|
||||||
goto out_reset;
|
goto out_reset;
|
||||||
|
|
||||||
x = xfrm_state_lookup(dev_net(skb->dev), skb->mark,
|
x = xfrm_input_state_lookup(dev_net(skb->dev), skb->mark,
|
||||||
(xfrm_address_t *)&ipv6_hdr(skb)->daddr,
|
(xfrm_address_t *)&ipv6_hdr(skb)->daddr,
|
||||||
spi, IPPROTO_ESP, AF_INET6);
|
spi, IPPROTO_ESP, AF_INET6);
|
||||||
|
|
||||||
if (unlikely(x && x->dir && x->dir != XFRM_SA_DIR_IN)) {
|
if (unlikely(x && x->dir && x->dir != XFRM_SA_DIR_IN)) {
|
||||||
/* non-offload path will record the error and audit log */
|
/* 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) {
|
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)) {
|
if (x && !xfrm_addr_equal(&x->id.daddr, xdaddr, family)) {
|
||||||
xfrm_state_put(x);
|
xfrm_state_put(x);
|
||||||
x = NULL;
|
x = NULL;
|
||||||
@ -1362,7 +1362,8 @@ static int pfkey_getspi(struct sock *sk, struct sk_buff *skb, const struct sadb_
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!x)
|
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)
|
if (x == NULL)
|
||||||
return -ENOENT;
|
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)
|
if (hdr->sadb_msg_seq == 0 || hdr->sadb_msg_errno == 0)
|
||||||
return 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)
|
if (x == NULL)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
@ -132,6 +132,7 @@ static const struct nla_policy compat_policy[XFRMA_MAX+1] = {
|
|||||||
[XFRMA_MTIMER_THRESH] = { .type = NLA_U32 },
|
[XFRMA_MTIMER_THRESH] = { .type = NLA_U32 },
|
||||||
[XFRMA_SA_DIR] = NLA_POLICY_RANGE(NLA_U8, XFRM_SA_DIR_IN, XFRM_SA_DIR_OUT),
|
[XFRMA_SA_DIR] = NLA_POLICY_RANGE(NLA_U8, XFRM_SA_DIR_IN, XFRM_SA_DIR_OUT),
|
||||||
[XFRMA_NAT_KEEPALIVE_INTERVAL] = { .type = NLA_U32 },
|
[XFRMA_NAT_KEEPALIVE_INTERVAL] = { .type = NLA_U32 },
|
||||||
|
[XFRMA_SA_PCPU] = { .type = NLA_U32 },
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct nlmsghdr *xfrm_nlmsg_put_compat(struct sk_buff *skb,
|
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_MTIMER_THRESH:
|
||||||
case XFRMA_SA_DIR:
|
case XFRMA_SA_DIR:
|
||||||
case XFRMA_NAT_KEEPALIVE_INTERVAL:
|
case XFRMA_NAT_KEEPALIVE_INTERVAL:
|
||||||
|
case XFRMA_SA_PCPU:
|
||||||
return xfrm_nla_cpy(dst, src, nla_len(src));
|
return xfrm_nla_cpy(dst, src, nla_len(src));
|
||||||
default:
|
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);
|
pr_warn_once("unsupported nla_type %d\n", src->nla_type);
|
||||||
return -EOPNOTSUPP;
|
return -EOPNOTSUPP;
|
||||||
}
|
}
|
||||||
@ -439,7 +441,7 @@ static int xfrm_xlate32_attr(void *dst, const struct nlattr *nla,
|
|||||||
int err;
|
int err;
|
||||||
|
|
||||||
if (type > XFRMA_MAX) {
|
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");
|
NL_SET_ERR_MSG(extack, "Bad attribute");
|
||||||
return -EOPNOTSUPP;
|
return -EOPNOTSUPP;
|
||||||
}
|
}
|
||||||
|
@ -572,7 +572,7 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type)
|
|||||||
goto drop;
|
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) {
|
if (x == NULL) {
|
||||||
secpath_reset(skb);
|
secpath_reset(skb);
|
||||||
XFRM_INC_STATS(net, LINUX_MIB_XFRMINNOSTATES);
|
XFRM_INC_STATS(net, LINUX_MIB_XFRMINNOSTATES);
|
||||||
|
@ -289,7 +289,7 @@ struct dst_entry *__xfrm_dst_lookup(int family,
|
|||||||
EXPORT_SYMBOL(__xfrm_dst_lookup);
|
EXPORT_SYMBOL(__xfrm_dst_lookup);
|
||||||
|
|
||||||
static inline struct dst_entry *xfrm_dst_lookup(struct xfrm_state *x,
|
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_saddr,
|
||||||
xfrm_address_t *prev_daddr,
|
xfrm_address_t *prev_daddr,
|
||||||
int family, u32 mark)
|
int family, u32 mark)
|
||||||
@ -312,7 +312,7 @@ static inline struct dst_entry *xfrm_dst_lookup(struct xfrm_state *x,
|
|||||||
params.net = net;
|
params.net = net;
|
||||||
params.saddr = saddr;
|
params.saddr = saddr;
|
||||||
params.daddr = daddr;
|
params.daddr = daddr;
|
||||||
params.tos = tos;
|
params.dscp = dscp;
|
||||||
params.oif = oif;
|
params.oif = oif;
|
||||||
params.mark = mark;
|
params.mark = mark;
|
||||||
params.ipproto = x->id.proto;
|
params.ipproto = x->id.proto;
|
||||||
@ -434,6 +434,7 @@ struct xfrm_policy *xfrm_policy_alloc(struct net *net, gfp_t gfp)
|
|||||||
if (policy) {
|
if (policy) {
|
||||||
write_pnet(&policy->xp_net, net);
|
write_pnet(&policy->xp_net, net);
|
||||||
INIT_LIST_HEAD(&policy->walk.all);
|
INIT_LIST_HEAD(&policy->walk.all);
|
||||||
|
INIT_HLIST_HEAD(&policy->state_cache_list);
|
||||||
INIT_HLIST_NODE(&policy->bydst);
|
INIT_HLIST_NODE(&policy->bydst);
|
||||||
INIT_HLIST_NODE(&policy->byidx);
|
INIT_HLIST_NODE(&policy->byidx);
|
||||||
rwlock_init(&policy->lock);
|
rwlock_init(&policy->lock);
|
||||||
@ -475,6 +476,9 @@ EXPORT_SYMBOL(xfrm_policy_destroy);
|
|||||||
|
|
||||||
static void xfrm_policy_kill(struct xfrm_policy *policy)
|
static void xfrm_policy_kill(struct xfrm_policy *policy)
|
||||||
{
|
{
|
||||||
|
struct net *net = xp_net(policy);
|
||||||
|
struct xfrm_state *x;
|
||||||
|
|
||||||
xfrm_dev_policy_delete(policy);
|
xfrm_dev_policy_delete(policy);
|
||||||
|
|
||||||
write_lock_bh(&policy->lock);
|
write_lock_bh(&policy->lock);
|
||||||
@ -490,6 +494,13 @@ static void xfrm_policy_kill(struct xfrm_policy *policy)
|
|||||||
if (del_timer(&policy->timer))
|
if (del_timer(&policy->timer))
|
||||||
xfrm_pol_put(policy);
|
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);
|
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)
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -2667,13 +2678,13 @@ static struct dst_entry *xfrm_bundle_create(struct xfrm_policy *policy,
|
|||||||
int header_len = 0;
|
int header_len = 0;
|
||||||
int nfheader_len = 0;
|
int nfheader_len = 0;
|
||||||
int trailer_len = 0;
|
int trailer_len = 0;
|
||||||
int tos;
|
|
||||||
int family = policy->selector.family;
|
int family = policy->selector.family;
|
||||||
xfrm_address_t saddr, daddr;
|
xfrm_address_t saddr, daddr;
|
||||||
|
dscp_t dscp;
|
||||||
|
|
||||||
xfrm_flowi_addr_get(fl, &saddr, &daddr, family);
|
xfrm_flowi_addr_get(fl, &saddr, &daddr, family);
|
||||||
|
|
||||||
tos = xfrm_get_tos(fl, family);
|
dscp = xfrm_get_dscp(fl, family);
|
||||||
|
|
||||||
dst_hold(dst);
|
dst_hold(dst);
|
||||||
|
|
||||||
@ -2721,8 +2732,8 @@ static struct dst_entry *xfrm_bundle_create(struct xfrm_policy *policy,
|
|||||||
family = xfrm[i]->props.family;
|
family = xfrm[i]->props.family;
|
||||||
|
|
||||||
oif = fl->flowi_oif ? : fl->flowi_l3mdev;
|
oif = fl->flowi_oif ? : fl->flowi_l3mdev;
|
||||||
dst = xfrm_dst_lookup(xfrm[i], tos, oif,
|
dst = xfrm_dst_lookup(xfrm[i], dscp, oif, &saddr,
|
||||||
&saddr, &daddr, family, mark);
|
&daddr, family, mark);
|
||||||
err = PTR_ERR(dst);
|
err = PTR_ERR(dst);
|
||||||
if (IS_ERR(dst))
|
if (IS_ERR(dst))
|
||||||
goto put_states;
|
goto put_states;
|
||||||
@ -3275,6 +3286,7 @@ no_transform:
|
|||||||
dst_release(dst);
|
dst_release(dst);
|
||||||
dst = dst_orig;
|
dst = dst_orig;
|
||||||
}
|
}
|
||||||
|
|
||||||
ok:
|
ok:
|
||||||
xfrm_pols_put(pols, drop_pols);
|
xfrm_pols_put(pols, drop_pols);
|
||||||
if (dst && dst->xfrm &&
|
if (dst && dst->xfrm &&
|
||||||
|
@ -665,6 +665,7 @@ struct xfrm_state *xfrm_state_alloc(struct net *net)
|
|||||||
refcount_set(&x->refcnt, 1);
|
refcount_set(&x->refcnt, 1);
|
||||||
atomic_set(&x->tunnel_users, 0);
|
atomic_set(&x->tunnel_users, 0);
|
||||||
INIT_LIST_HEAD(&x->km.all);
|
INIT_LIST_HEAD(&x->km.all);
|
||||||
|
INIT_HLIST_NODE(&x->state_cache);
|
||||||
INIT_HLIST_NODE(&x->bydst);
|
INIT_HLIST_NODE(&x->bydst);
|
||||||
INIT_HLIST_NODE(&x->bysrc);
|
INIT_HLIST_NODE(&x->bysrc);
|
||||||
INIT_HLIST_NODE(&x->byspi);
|
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->lft.hard_packet_limit = XFRM_INF;
|
||||||
x->replay_maxage = 0;
|
x->replay_maxage = 0;
|
||||||
x->replay_maxdiff = 0;
|
x->replay_maxdiff = 0;
|
||||||
|
x->pcpu_num = UINT_MAX;
|
||||||
spin_lock_init(&x->lock);
|
spin_lock_init(&x->lock);
|
||||||
}
|
}
|
||||||
return x;
|
return x;
|
||||||
@ -743,12 +745,18 @@ int __xfrm_state_delete(struct xfrm_state *x)
|
|||||||
|
|
||||||
if (x->km.state != XFRM_STATE_DEAD) {
|
if (x->km.state != XFRM_STATE_DEAD) {
|
||||||
x->km.state = XFRM_STATE_DEAD;
|
x->km.state = XFRM_STATE_DEAD;
|
||||||
|
|
||||||
spin_lock(&net->xfrm.xfrm_state_lock);
|
spin_lock(&net->xfrm.xfrm_state_lock);
|
||||||
list_del(&x->km.all);
|
list_del(&x->km.all);
|
||||||
hlist_del_rcu(&x->bydst);
|
hlist_del_rcu(&x->bydst);
|
||||||
hlist_del_rcu(&x->bysrc);
|
hlist_del_rcu(&x->bysrc);
|
||||||
if (x->km.seq)
|
if (x->km.seq)
|
||||||
hlist_del_rcu(&x->byseq);
|
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)
|
if (x->id.spi)
|
||||||
hlist_del_rcu(&x->byspi);
|
hlist_del_rcu(&x->byspi);
|
||||||
net->xfrm.state_num--;
|
net->xfrm.state_num--;
|
||||||
@ -1101,6 +1109,52 @@ static struct xfrm_state *__xfrm_state_lookup(struct net *net, u32 mark,
|
|||||||
return NULL;
|
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,
|
static struct xfrm_state *__xfrm_state_lookup_byaddr(struct net *net, u32 mark,
|
||||||
const xfrm_address_t *daddr,
|
const xfrm_address_t *daddr,
|
||||||
const xfrm_address_t *saddr,
|
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,
|
struct xfrm_state **best, int *acq_in_progress,
|
||||||
int *error)
|
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:
|
/* Resolution logic:
|
||||||
* 1. There is a valid state with matching selector. Done.
|
* 1. There is a valid state with matching selector. Done.
|
||||||
* 2. Valid state with inappropriate selector. Skip.
|
* 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))
|
&fl->u.__fl_common))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if (x->pcpu_num != UINT_MAX && x->pcpu_num != pcpu_id)
|
||||||
|
return;
|
||||||
|
|
||||||
if (!*best ||
|
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)->km.dying == x->km.dying &&
|
((*best)->km.dying == x->km.dying &&
|
||||||
(*best)->curlft.add_time < x->curlft.add_time))
|
(*best)->curlft.add_time < x->curlft.add_time))
|
||||||
*best = x;
|
*best = x;
|
||||||
} else if (x->km.state == XFRM_STATE_ACQ) {
|
} 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 ||
|
} else if (x->km.state == XFRM_STATE_ERROR ||
|
||||||
x->km.state == XFRM_STATE_EXPIRED) {
|
x->km.state == XFRM_STATE_EXPIRED) {
|
||||||
if ((!x->sel.family ||
|
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 short encap_family = tmpl->encap_family;
|
||||||
unsigned int sequence;
|
unsigned int sequence;
|
||||||
struct km_event c;
|
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;
|
to_put = NULL;
|
||||||
|
|
||||||
sequence = read_seqcount_begin(&net->xfrm.xfrm_state_hash_generation);
|
sequence = read_seqcount_begin(&net->xfrm.xfrm_state_hash_generation);
|
||||||
|
|
||||||
rcu_read_lock();
|
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);
|
h = xfrm_dst_hash(net, daddr, saddr, tmpl->reqid, encap_family);
|
||||||
hlist_for_each_entry_rcu(x, net->xfrm.state_bydst + h, bydst) {
|
hlist_for_each_entry_rcu(x, net->xfrm.state_bydst + h, bydst) {
|
||||||
#ifdef CONFIG_XFRM_OFFLOAD
|
#ifdef CONFIG_XFRM_OFFLOAD
|
||||||
@ -1282,7 +1395,10 @@ xfrm_state_find(const xfrm_address_t *daddr, const xfrm_address_t *saddr,
|
|||||||
}
|
}
|
||||||
|
|
||||||
found:
|
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 (!x && !error && !acquire_in_progress) {
|
||||||
if (tmpl->id.spi &&
|
if (tmpl->id.spi &&
|
||||||
(x0 = __xfrm_state_lookup_all(net, mark, daddr,
|
(x0 = __xfrm_state_lookup_all(net, mark, daddr,
|
||||||
@ -1314,6 +1430,8 @@ found:
|
|||||||
xfrm_init_tempstate(x, fl, tmpl, daddr, saddr, family);
|
xfrm_init_tempstate(x, fl, tmpl, daddr, saddr, family);
|
||||||
memcpy(&x->mark, &pol->mark, sizeof(x->mark));
|
memcpy(&x->mark, &pol->mark, sizeof(x->mark));
|
||||||
x->if_id = if_id;
|
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);
|
error = security_xfrm_state_alloc_acquire(x, pol->security, fl->flowi_secid);
|
||||||
if (error) {
|
if (error) {
|
||||||
@ -1352,6 +1470,7 @@ found:
|
|||||||
x->km.state = XFRM_STATE_ACQ;
|
x->km.state = XFRM_STATE_ACQ;
|
||||||
x->dir = XFRM_SA_DIR_OUT;
|
x->dir = XFRM_SA_DIR_OUT;
|
||||||
list_add(&x->km.all, &net->xfrm.state_all);
|
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,
|
XFRM_STATE_INSERT(bydst, &x->bydst,
|
||||||
net->xfrm.state_bydst + h,
|
net->xfrm.state_bydst + h,
|
||||||
x->xso.type);
|
x->xso.type);
|
||||||
@ -1359,6 +1478,7 @@ found:
|
|||||||
XFRM_STATE_INSERT(bysrc, &x->bysrc,
|
XFRM_STATE_INSERT(bysrc, &x->bysrc,
|
||||||
net->xfrm.state_bysrc + h,
|
net->xfrm.state_bysrc + h,
|
||||||
x->xso.type);
|
x->xso.type);
|
||||||
|
INIT_HLIST_NODE(&x->state_cache);
|
||||||
if (x->id.spi) {
|
if (x->id.spi) {
|
||||||
h = xfrm_spi_hash(net, &x->id.daddr, x->id.spi, x->id.proto, encap_family);
|
h = xfrm_spi_hash(net, &x->id.daddr, x->id.spi, x->id.proto, encap_family);
|
||||||
XFRM_STATE_INSERT(byspi, &x->byspi,
|
XFRM_STATE_INSERT(byspi, &x->byspi,
|
||||||
@ -1392,6 +1512,11 @@ found:
|
|||||||
x = NULL;
|
x = NULL;
|
||||||
error = -ESRCH;
|
error = -ESRCH;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Use the already installed 'fallback' while the CPU-specific
|
||||||
|
* SA acquire is handled*/
|
||||||
|
if (best)
|
||||||
|
x = best;
|
||||||
}
|
}
|
||||||
out:
|
out:
|
||||||
if (x) {
|
if (x) {
|
||||||
@ -1402,6 +1527,15 @@ out:
|
|||||||
} else {
|
} else {
|
||||||
*err = acquire_in_progress ? -EAGAIN : error;
|
*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();
|
rcu_read_unlock();
|
||||||
if (to_put)
|
if (to_put)
|
||||||
xfrm_state_put(to_put);
|
xfrm_state_put(to_put);
|
||||||
@ -1524,12 +1658,14 @@ static void __xfrm_state_bump_genids(struct xfrm_state *xnew)
|
|||||||
unsigned int h;
|
unsigned int h;
|
||||||
u32 mark = xnew->mark.v & xnew->mark.m;
|
u32 mark = xnew->mark.v & xnew->mark.m;
|
||||||
u32 if_id = xnew->if_id;
|
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);
|
h = xfrm_dst_hash(net, &xnew->id.daddr, &xnew->props.saddr, reqid, family);
|
||||||
hlist_for_each_entry(x, net->xfrm.state_bydst+h, bydst) {
|
hlist_for_each_entry(x, net->xfrm.state_bydst+h, bydst) {
|
||||||
if (x->props.family == family &&
|
if (x->props.family == family &&
|
||||||
x->props.reqid == reqid &&
|
x->props.reqid == reqid &&
|
||||||
x->if_id == if_id &&
|
x->if_id == if_id &&
|
||||||
|
x->pcpu_num == cpu_id &&
|
||||||
(mark & x->mark.m) == x->mark.v &&
|
(mark & x->mark.m) == x->mark.v &&
|
||||||
xfrm_addr_equal(&x->id.daddr, &xnew->id.daddr, family) &&
|
xfrm_addr_equal(&x->id.daddr, &xnew->id.daddr, family) &&
|
||||||
xfrm_addr_equal(&x->props.saddr, &xnew->props.saddr, 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,
|
static struct xfrm_state *__find_acq_core(struct net *net,
|
||||||
const struct xfrm_mark *m,
|
const struct xfrm_mark *m,
|
||||||
unsigned short family, u8 mode,
|
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 *daddr,
|
||||||
const xfrm_address_t *saddr,
|
const xfrm_address_t *saddr,
|
||||||
int create)
|
int create)
|
||||||
@ -1569,6 +1705,7 @@ static struct xfrm_state *__find_acq_core(struct net *net,
|
|||||||
x->id.spi != 0 ||
|
x->id.spi != 0 ||
|
||||||
x->id.proto != proto ||
|
x->id.proto != proto ||
|
||||||
(mark & x->mark.m) != x->mark.v ||
|
(mark & x->mark.m) != x->mark.v ||
|
||||||
|
x->pcpu_num != pcpu_num ||
|
||||||
!xfrm_addr_equal(&x->id.daddr, daddr, family) ||
|
!xfrm_addr_equal(&x->id.daddr, daddr, family) ||
|
||||||
!xfrm_addr_equal(&x->props.saddr, saddr, family))
|
!xfrm_addr_equal(&x->props.saddr, saddr, family))
|
||||||
continue;
|
continue;
|
||||||
@ -1602,6 +1739,7 @@ static struct xfrm_state *__find_acq_core(struct net *net,
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
x->pcpu_num = pcpu_num;
|
||||||
x->km.state = XFRM_STATE_ACQ;
|
x->km.state = XFRM_STATE_ACQ;
|
||||||
x->id.proto = proto;
|
x->id.proto = proto;
|
||||||
x->props.family = family;
|
x->props.family = family;
|
||||||
@ -1630,7 +1768,7 @@ static struct xfrm_state *__find_acq_core(struct net *net,
|
|||||||
return x;
|
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)
|
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) {
|
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) ||
|
if (x1 && ((x1->id.proto != x->id.proto) ||
|
||||||
!xfrm_addr_equal(&x1->id.daddr, &x->id.daddr, family))) {
|
!xfrm_addr_equal(&x1->id.daddr, &x->id.daddr, family))) {
|
||||||
to_put = x1;
|
to_put = x1;
|
||||||
@ -1666,7 +1804,7 @@ int xfrm_state_add(struct xfrm_state *x)
|
|||||||
|
|
||||||
if (use_spi && !x1)
|
if (use_spi && !x1)
|
||||||
x1 = __find_acq_core(net, &x->mark, family, x->props.mode,
|
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);
|
&x->id.daddr, &x->props.saddr, 0);
|
||||||
|
|
||||||
__xfrm_state_bump_genids(x);
|
__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.flags = orig->props.flags;
|
||||||
x->props.extra_flags = orig->props.extra_flags;
|
x->props.extra_flags = orig->props.extra_flags;
|
||||||
|
|
||||||
|
x->pcpu_num = orig->pcpu_num;
|
||||||
x->if_id = orig->if_id;
|
x->if_id = orig->if_id;
|
||||||
x->tfcpad = orig->tfcpad;
|
x->tfcpad = orig->tfcpad;
|
||||||
x->replay_maxdiff = orig->replay_maxdiff;
|
x->replay_maxdiff = orig->replay_maxdiff;
|
||||||
@ -2066,13 +2205,14 @@ EXPORT_SYMBOL(xfrm_state_lookup_byaddr);
|
|||||||
|
|
||||||
struct xfrm_state *
|
struct xfrm_state *
|
||||||
xfrm_find_acq(struct net *net, const struct xfrm_mark *mark, u8 mode, u32 reqid,
|
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)
|
const xfrm_address_t *saddr, int create, unsigned short family)
|
||||||
{
|
{
|
||||||
struct xfrm_state *x;
|
struct xfrm_state *x;
|
||||||
|
|
||||||
spin_lock_bh(&net->xfrm.xfrm_state_lock);
|
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);
|
spin_unlock_bh(&net->xfrm.xfrm_state_lock);
|
||||||
|
|
||||||
return x;
|
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 */
|
/* 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);
|
unsigned int h = xfrm_seq_hash(net, seq);
|
||||||
struct xfrm_state *x;
|
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) {
|
hlist_for_each_entry_rcu(x, net->xfrm.state_byseq + h, byseq) {
|
||||||
if (x->km.seq == seq &&
|
if (x->km.seq == seq &&
|
||||||
(mark & x->mark.m) == x->mark.v &&
|
(mark & x->mark.m) == x->mark.v &&
|
||||||
|
x->pcpu_num == pcpu_num &&
|
||||||
x->km.state == XFRM_STATE_ACQ) {
|
x->km.state == XFRM_STATE_ACQ) {
|
||||||
xfrm_state_hold(x);
|
xfrm_state_hold(x);
|
||||||
return x;
|
return x;
|
||||||
@ -2224,12 +2365,12 @@ static struct xfrm_state *__xfrm_find_acq_byseq(struct net *net, u32 mark, u32 s
|
|||||||
return NULL;
|
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;
|
struct xfrm_state *x;
|
||||||
|
|
||||||
spin_lock_bh(&net->xfrm.xfrm_state_lock);
|
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);
|
spin_unlock_bh(&net->xfrm.xfrm_state_lock);
|
||||||
return x;
|
return x;
|
||||||
}
|
}
|
||||||
@ -2988,6 +3129,11 @@ int __net_init xfrm_state_init(struct net *net)
|
|||||||
net->xfrm.state_byseq = xfrm_hash_alloc(sz);
|
net->xfrm.state_byseq = xfrm_hash_alloc(sz);
|
||||||
if (!net->xfrm.state_byseq)
|
if (!net->xfrm.state_byseq)
|
||||||
goto out_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_hmask = ((sz / sizeof(struct hlist_head)) - 1);
|
||||||
|
|
||||||
net->xfrm.state_num = 0;
|
net->xfrm.state_num = 0;
|
||||||
@ -2997,6 +3143,8 @@ int __net_init xfrm_state_init(struct net *net)
|
|||||||
&net->xfrm.xfrm_state_lock);
|
&net->xfrm.xfrm_state_lock);
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
out_state_cache_input:
|
||||||
|
xfrm_hash_free(net->xfrm.state_byseq, sz);
|
||||||
out_byseq:
|
out_byseq:
|
||||||
xfrm_hash_free(net->xfrm.state_byspi, sz);
|
xfrm_hash_free(net->xfrm.state_byspi, sz);
|
||||||
out_byspi:
|
out_byspi:
|
||||||
@ -3026,6 +3174,7 @@ void xfrm_state_fini(struct net *net)
|
|||||||
xfrm_hash_free(net->xfrm.state_bysrc, sz);
|
xfrm_hash_free(net->xfrm.state_bysrc, sz);
|
||||||
WARN_ON(!hlist_empty(net->xfrm.state_bydst));
|
WARN_ON(!hlist_empty(net->xfrm.state_bydst));
|
||||||
xfrm_hash_free(net->xfrm.state_bydst, sz);
|
xfrm_hash_free(net->xfrm.state_bydst, sz);
|
||||||
|
free_percpu(net->xfrm.state_cache_input);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_AUDITSYSCALL
|
#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:
|
out:
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
@ -839,6 +845,12 @@ static struct xfrm_state *xfrm_state_construct(struct net *net,
|
|||||||
x->nat_keepalive_interval =
|
x->nat_keepalive_interval =
|
||||||
nla_get_u32(attrs[XFRMA_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);
|
err = __xfrm_init_state(x, false, attrs[XFRMA_OFFLOAD_DEV], extack);
|
||||||
if (err)
|
if (err)
|
||||||
goto error;
|
goto error;
|
||||||
@ -1087,7 +1099,7 @@ static int copy_to_user_auth(struct xfrm_algo_auth *auth, struct sk_buff *skb)
|
|||||||
if (!nla)
|
if (!nla)
|
||||||
return -EMSGSIZE;
|
return -EMSGSIZE;
|
||||||
algo = nla_data(nla);
|
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)
|
if (redact_secret && auth->alg_key_len)
|
||||||
memset(algo->alg_key, 0, (auth->alg_key_len + 7) / 8);
|
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)
|
if (ret)
|
||||||
goto out;
|
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)
|
if (x->dir)
|
||||||
ret = nla_put_u8(skb, XFRMA_SA_DIR, 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;
|
u32 mark;
|
||||||
struct xfrm_mark m;
|
struct xfrm_mark m;
|
||||||
u32 if_id = 0;
|
u32 if_id = 0;
|
||||||
|
u32 pcpu_num = UINT_MAX;
|
||||||
|
|
||||||
p = nlmsg_data(nlh);
|
p = nlmsg_data(nlh);
|
||||||
err = verify_spi_info(p->info.id.proto, p->min, p->max, extack);
|
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 (attrs[XFRMA_IF_ID])
|
||||||
if_id = nla_get_u32(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) {
|
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)) {
|
if (x && !xfrm_addr_equal(&x->id.daddr, daddr, family)) {
|
||||||
xfrm_state_put(x);
|
xfrm_state_put(x);
|
||||||
x = NULL;
|
x = NULL;
|
||||||
@ -1724,7 +1750,7 @@ static int xfrm_alloc_userspi(struct sk_buff *skb, struct nlmsghdr *nlh,
|
|||||||
|
|
||||||
if (!x)
|
if (!x)
|
||||||
x = xfrm_find_acq(net, &m, p->info.mode, p->info.reqid,
|
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,
|
&p->info.saddr, 1,
|
||||||
family);
|
family);
|
||||||
err = -ENOENT;
|
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(sizeof(struct xfrm_mark))
|
||||||
+ nla_total_size(4) /* XFRM_AE_RTHR */
|
+ nla_total_size(4) /* XFRM_AE_RTHR */
|
||||||
+ nla_total_size(4) /* XFRM_AE_ETHR */
|
+ 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)
|
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);
|
err = xfrm_if_id_put(skb, x->if_id);
|
||||||
if (err)
|
if (err)
|
||||||
goto out_cancel;
|
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) {
|
if (x->dir) {
|
||||||
err = nla_put_u8(skb, XFRMA_SA_DIR, 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);
|
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);
|
err = verify_newpolicy_info(&ua->policy, extack);
|
||||||
if (err)
|
if (err)
|
||||||
goto free_state;
|
goto free_state;
|
||||||
@ -3180,6 +3219,7 @@ const struct nla_policy xfrma_policy[XFRMA_MAX+1] = {
|
|||||||
[XFRMA_MTIMER_THRESH] = { .type = NLA_U32 },
|
[XFRMA_MTIMER_THRESH] = { .type = NLA_U32 },
|
||||||
[XFRMA_SA_DIR] = NLA_POLICY_RANGE(NLA_U8, XFRM_SA_DIR_IN, XFRM_SA_DIR_OUT),
|
[XFRMA_SA_DIR] = NLA_POLICY_RANGE(NLA_U8, XFRM_SA_DIR_IN, XFRM_SA_DIR_OUT),
|
||||||
[XFRMA_NAT_KEEPALIVE_INTERVAL] = { .type = NLA_U32 },
|
[XFRMA_NAT_KEEPALIVE_INTERVAL] = { .type = NLA_U32 },
|
||||||
|
[XFRMA_SA_PCPU] = { .type = NLA_U32 },
|
||||||
};
|
};
|
||||||
EXPORT_SYMBOL_GPL(xfrma_policy);
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3346,7 +3400,8 @@ static inline unsigned int xfrm_expire_msgsize(void)
|
|||||||
{
|
{
|
||||||
return NLMSG_ALIGN(sizeof(struct xfrm_user_expire)) +
|
return NLMSG_ALIGN(sizeof(struct xfrm_user_expire)) +
|
||||||
nla_total_size(sizeof(struct xfrm_mark)) +
|
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)
|
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);
|
err = xfrm_if_id_put(skb, x->if_id);
|
||||||
if (err)
|
if (err)
|
||||||
return 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) {
|
if (x->dir) {
|
||||||
err = nla_put_u8(skb, XFRMA_SA_DIR, 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)
|
if (x->if_id)
|
||||||
l += nla_total_size(sizeof(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. */
|
/* Must count x->lastused as it may become non-zero behind our back. */
|
||||||
l += nla_total_size_64bit(sizeof(u64));
|
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_user_tmpl) * xp->xfrm_nr)
|
||||||
+ nla_total_size(sizeof(struct xfrm_mark))
|
+ nla_total_size(sizeof(struct xfrm_mark))
|
||||||
+ nla_total_size(xfrm_user_sec_ctx_size(x->security))
|
+ nla_total_size(xfrm_user_sec_ctx_size(x->security))
|
||||||
|
+ nla_total_size(4) /* XFRMA_SA_PCPU */
|
||||||
+ userpolicy_type_attrsize();
|
+ 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);
|
err = xfrm_if_id_put(skb, xp->if_id);
|
||||||
if (!err && xp->xdo.dev)
|
if (!err && xp->xdo.dev)
|
||||||
err = copy_user_offload(&xp->xdo, skb);
|
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) {
|
if (err) {
|
||||||
nlmsg_cancel(skb, nlh);
|
nlmsg_cancel(skb, nlh);
|
||||||
return err;
|
return err;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user