xfrm: add XFRM_MSG_MIGRATE_STATE for single SA migration

Add a new netlink method to migrate a single xfrm_state.
Unlike the existing migration mechanism (SA + policy), this
supports migrating only the SA and allows changing the reqid.

The SA is looked up via xfrm_usersa_id, which uniquely
identifies it, so old_saddr is not needed. old_daddr is carried in
xfrm_usersa_id.daddr.

The reqid is invariant in the old migration.

Signed-off-by: Antony Antony <antony.antony@secunet.com>
Signed-off-by: Steffen Klassert <steffen.klassert@secunet.com>
This commit is contained in:
Antony Antony 2026-05-26 21:09:51 +02:00 committed by Steffen Klassert
parent 8eed5ba257
commit a9d155ea9b
7 changed files with 405 additions and 17 deletions

View File

@ -684,12 +684,20 @@ struct xfrm_migrate {
xfrm_address_t new_saddr;
struct xfrm_encap_tmpl *encap;
struct xfrm_user_offload *xuo;
struct xfrm_mark old_mark;
const struct xfrm_mark *new_mark;
struct xfrm_mark smark;
u8 proto;
u8 mode;
u16 reserved;
u16 msg_type; /* XFRM_MSG_MIGRATE or XFRM_MSG_MIGRATE_STATE */
u32 flags;
u32 old_reqid;
u32 new_reqid;
u32 nat_keepalive_interval;
u32 mapping_maxage;
u16 old_family;
u16 new_family;
const struct xfrm_selector *new_sel;
};
#define XFRM_KM_TIMEOUT 30

View File

@ -227,6 +227,9 @@ enum {
#define XFRM_MSG_SETDEFAULT XFRM_MSG_SETDEFAULT
XFRM_MSG_GETDEFAULT,
#define XFRM_MSG_GETDEFAULT XFRM_MSG_GETDEFAULT
XFRM_MSG_MIGRATE_STATE,
#define XFRM_MSG_MIGRATE_STATE XFRM_MSG_MIGRATE_STATE
__XFRM_MSG_MAX
};
#define XFRM_MSG_MAX (__XFRM_MSG_MAX - 1)
@ -507,6 +510,28 @@ struct xfrm_user_migrate {
__u16 new_family;
};
struct xfrm_user_migrate_state {
struct xfrm_usersa_id id;
xfrm_address_t new_daddr;
xfrm_address_t new_saddr;
struct xfrm_mark old_mark;
struct xfrm_selector new_sel;
__u32 new_reqid;
__u32 flags;
__u16 new_family;
__u16 reserved;
};
/* Flags for xfrm_user_migrate_state.flags */
enum xfrm_migrate_state_flags {
XFRM_MIGRATE_STATE_CLEAR_OFFLOAD = 1, /* do not inherit offload from existing SA */
XFRM_MIGRATE_STATE_UPDATE_H2H_SEL = 2, /* update H2H selector from new daddr/saddr */
};
/* All flags defined as of this header version; unknown bits are rejected. */
#define XFRM_MIGRATE_STATE_KNOWN_FLAGS \
(XFRM_MIGRATE_STATE_CLEAR_OFFLOAD | XFRM_MIGRATE_STATE_UPDATE_H2H_SEL)
struct xfrm_user_mapping {
struct xfrm_usersa_id id;
__u32 reqid;

View File

@ -94,7 +94,8 @@ static const int compat_msg_min[XFRM_NR_MSGTYPES] = {
[XFRM_MSG_GETSADINFO - XFRM_MSG_BASE] = sizeof(u32),
[XFRM_MSG_NEWSPDINFO - XFRM_MSG_BASE] = sizeof(u32),
[XFRM_MSG_GETSPDINFO - XFRM_MSG_BASE] = sizeof(u32),
[XFRM_MSG_MAPPING - XFRM_MSG_BASE] = XMSGSIZE(xfrm_user_mapping)
[XFRM_MSG_MAPPING - XFRM_MSG_BASE] = XMSGSIZE(xfrm_user_mapping),
[XFRM_MSG_MIGRATE_STATE - XFRM_MSG_BASE] = XMSGSIZE(xfrm_user_migrate_state),
};
static const struct nla_policy compat_policy[XFRMA_MAX+1] = {
@ -162,6 +163,7 @@ static struct nlmsghdr *xfrm_nlmsg_put_compat(struct sk_buff *skb,
case XFRM_MSG_NEWAE:
case XFRM_MSG_REPORT:
case XFRM_MSG_MIGRATE:
case XFRM_MSG_MIGRATE_STATE:
case XFRM_MSG_NEWSADINFO:
case XFRM_MSG_NEWSPDINFO:
case XFRM_MSG_MAPPING:
@ -498,6 +500,7 @@ static int xfrm_xlate32(struct nlmsghdr *dst, const struct nlmsghdr *src,
case XFRM_MSG_GETAE:
case XFRM_MSG_REPORT:
case XFRM_MSG_MIGRATE:
case XFRM_MSG_MIGRATE_STATE:
case XFRM_MSG_NEWSADINFO:
case XFRM_MSG_GETSADINFO:
case XFRM_MSG_NEWSPDINFO:

View File

@ -4643,6 +4643,21 @@ static int xfrm_migrate_check(const struct xfrm_migrate *m, int num_migrate,
return 0;
}
/*
* Fill migrate fields that are invariant in XFRM_MSG_MIGRATE: inherited
* from the existing SA unchanged. XFRM_MSG_MIGRATE_STATE can update these.
*/
static void xfrm_migrate_copy_old(const struct xfrm_state *x,
struct xfrm_migrate *mp)
{
mp->msg_type = XFRM_MSG_MIGRATE;
mp->smark = x->props.smark;
mp->new_reqid = x->props.reqid;
mp->nat_keepalive_interval = x->nat_keepalive_interval;
mp->mapping_maxage = x->mapping_maxage;
mp->new_mark = &x->mark;
}
int xfrm_migrate(const struct xfrm_selector *sel, u8 dir, u8 type,
struct xfrm_migrate *m, int num_migrate,
struct xfrm_kmaddress *k, struct net *net,
@ -4682,6 +4697,8 @@ int xfrm_migrate(const struct xfrm_selector *sel, u8 dir, u8 type,
nx_cur++;
mp->encap = encap;
mp->xuo = xuo;
xfrm_migrate_copy_old(x, mp);
xc = xfrm_state_migrate(x, mp, net, extack);
if (xc) {
x_new[nx_new] = xc;

View File

@ -1974,11 +1974,25 @@ static struct xfrm_state *xfrm_state_clone_and_setup(struct xfrm_state *orig,
goto out;
memcpy(&x->id, &orig->id, sizeof(x->id));
memcpy(&x->sel, &orig->sel, sizeof(x->sel));
if (m->msg_type == XFRM_MSG_MIGRATE_STATE) {
if (m->flags & XFRM_MIGRATE_STATE_UPDATE_H2H_SEL) {
u8 prefixlen = (m->new_family == AF_INET6) ? 128 : 32;
x->sel = orig->sel;
x->sel.family = m->new_family;
x->sel.prefixlen_d = prefixlen;
x->sel.prefixlen_s = prefixlen;
x->sel.daddr = m->new_daddr;
x->sel.saddr = m->new_saddr;
} else {
x->sel = *m->new_sel;
}
} else {
x->sel = orig->sel;
}
memcpy(&x->lft, &orig->lft, sizeof(x->lft));
x->props.mode = orig->props.mode;
x->props.replay_window = orig->props.replay_window;
x->props.reqid = orig->props.reqid;
if (orig->aalg) {
x->aalg = xfrm_algo_auth_clone(orig->aalg);
@ -2011,8 +2025,8 @@ static struct xfrm_state *xfrm_state_clone_and_setup(struct xfrm_state *orig,
x->encap = kmemdup(m->encap, sizeof(*x->encap), GFP_KERNEL);
if (!x->encap)
goto error;
x->mapping_maxage = orig->mapping_maxage;
x->nat_keepalive_interval = orig->nat_keepalive_interval;
x->mapping_maxage = m->mapping_maxage;
x->nat_keepalive_interval = m->nat_keepalive_interval;
}
if (orig->security)
@ -2029,8 +2043,9 @@ static struct xfrm_state *xfrm_state_clone_and_setup(struct xfrm_state *orig,
if (xfrm_replay_clone(x, orig))
goto error;
memcpy(&x->mark, &orig->mark, sizeof(x->mark));
memcpy(&x->props.smark, &orig->props.smark, sizeof(x->props.smark));
x->mark = m->new_mark ? *m->new_mark : m->old_mark;
x->props.smark = m->smark;
x->props.flags = orig->props.flags;
x->props.extra_flags = orig->props.extra_flags;
@ -2053,7 +2068,7 @@ static struct xfrm_state *xfrm_state_clone_and_setup(struct xfrm_state *orig,
goto error;
}
x->props.reqid = m->new_reqid;
x->props.family = m->new_family;
memcpy(&x->id.daddr, &m->new_daddr, sizeof(x->id.daddr));
memcpy(&x->props.saddr, &m->new_saddr, sizeof(x->props.saddr));

View File

@ -1201,6 +1201,16 @@ static int copy_sec_ctx(struct xfrm_sec_ctx *s, struct sk_buff *skb)
return 0;
}
static void xso_to_xuo(const struct xfrm_dev_offload *xso,
struct xfrm_user_offload *xuo)
{
xuo->ifindex = xso->dev->ifindex;
if (xso->dir == XFRM_DEV_OFFLOAD_IN)
xuo->flags = XFRM_OFFLOAD_INBOUND;
if (xso->type == XFRM_DEV_OFFLOAD_PACKET)
xuo->flags |= XFRM_OFFLOAD_PACKET;
}
static int copy_user_offload(struct xfrm_dev_offload *xso, struct sk_buff *skb)
{
struct xfrm_user_offload *xuo;
@ -1212,11 +1222,7 @@ static int copy_user_offload(struct xfrm_dev_offload *xso, struct sk_buff *skb)
xuo = nla_data(attr);
memset(xuo, 0, sizeof(*xuo));
xuo->ifindex = xso->dev->ifindex;
if (xso->dir == XFRM_DEV_OFFLOAD_IN)
xuo->flags = XFRM_OFFLOAD_INBOUND;
if (xso->type == XFRM_DEV_OFFLOAD_PACKET)
xuo->flags |= XFRM_OFFLOAD_PACKET;
xso_to_xuo(xso, xuo);
return 0;
}
@ -1341,7 +1347,7 @@ static int copy_to_user_encap(struct xfrm_encap_tmpl *ep, struct sk_buff *skb)
return 0;
}
static int xfrm_smark_put(struct sk_buff *skb, struct xfrm_mark *m)
static int xfrm_smark_put(struct sk_buff *skb, const struct xfrm_mark *m)
{
int ret = 0;
@ -3090,6 +3096,25 @@ static int xfrm_add_acquire(struct sk_buff *skb, struct nlmsghdr *nlh,
}
#ifdef CONFIG_XFRM_MIGRATE
static void copy_from_user_migrate_state(struct xfrm_migrate *ma,
const struct xfrm_user_migrate_state *um)
{
memcpy(&ma->old_daddr, &um->id.daddr, sizeof(ma->old_daddr));
memcpy(&ma->new_daddr, &um->new_daddr, sizeof(ma->new_daddr));
memcpy(&ma->new_saddr, &um->new_saddr, sizeof(ma->new_saddr));
ma->proto = um->id.proto;
ma->new_reqid = um->new_reqid;
ma->old_family = um->id.family;
ma->new_family = um->new_family;
ma->old_mark = um->old_mark;
ma->flags = um->flags;
ma->new_sel = &um->new_sel;
ma->msg_type = XFRM_MSG_MIGRATE_STATE;
}
static int copy_from_user_migrate(struct xfrm_migrate *ma,
struct xfrm_kmaddress *k,
struct nlattr **attrs, int *num,
@ -3129,6 +3154,7 @@ static int copy_from_user_migrate(struct xfrm_migrate *ma,
ma->old_family = um->old_family;
ma->new_family = um->new_family;
ma->msg_type = XFRM_MSG_MIGRATE;
}
*num = i;
@ -3139,7 +3165,7 @@ static int xfrm_do_migrate(struct sk_buff *skb, struct nlmsghdr *nlh,
struct nlattr **attrs, struct netlink_ext_ack *extack)
{
struct xfrm_userpolicy_id *pi = nlmsg_data(nlh);
struct xfrm_migrate m[XFRM_MAX_DEPTH];
struct xfrm_migrate m[XFRM_MAX_DEPTH] = {};
struct xfrm_kmaddress km, *kmp;
u8 type;
int err;
@ -3192,7 +3218,298 @@ static int xfrm_do_migrate(struct sk_buff *skb, struct nlmsghdr *nlh,
kfree(xuo);
return err;
}
static int build_migrate_state(struct sk_buff *skb,
const struct xfrm_user_migrate_state *um,
const struct xfrm_migrate *m,
u8 dir, u32 portid, u32 seq)
{
int err;
struct nlmsghdr *nlh;
struct xfrm_user_migrate_state *hdr;
nlh = nlmsg_put(skb, portid, seq, XFRM_MSG_MIGRATE_STATE,
sizeof(struct xfrm_user_migrate_state), 0);
if (!nlh)
return -EMSGSIZE;
hdr = nlmsg_data(nlh);
*hdr = *um;
hdr->new_sel = *m->new_sel;
if (m->encap) {
err = nla_put(skb, XFRMA_ENCAP, sizeof(*m->encap), m->encap);
if (err)
goto out_cancel;
}
if (m->xuo) {
err = nla_put(skb, XFRMA_OFFLOAD_DEV, sizeof(*m->xuo), m->xuo);
if (err)
goto out_cancel;
}
if (m->new_mark) {
err = nla_put(skb, XFRMA_MARK, sizeof(*m->new_mark),
m->new_mark);
if (err)
goto out_cancel;
}
err = xfrm_smark_put(skb, &m->smark);
if (err)
goto out_cancel;
if (m->mapping_maxage) {
err = nla_put_u32(skb, XFRMA_MTIMER_THRESH, m->mapping_maxage);
if (err)
goto out_cancel;
}
if (m->nat_keepalive_interval) {
err = nla_put_u32(skb, XFRMA_NAT_KEEPALIVE_INTERVAL,
m->nat_keepalive_interval);
if (err)
goto out_cancel;
}
if (dir) {
err = nla_put_u8(skb, XFRMA_SA_DIR, dir);
if (err)
goto out_cancel;
}
nlmsg_end(skb, nlh);
return 0;
out_cancel:
nlmsg_cancel(skb, nlh);
return err;
}
static unsigned int xfrm_migrate_state_msgsize(const struct xfrm_migrate *m,
u8 dir)
{
return NLMSG_ALIGN(sizeof(struct xfrm_user_migrate_state)) +
(m->encap ? nla_total_size(sizeof(struct xfrm_encap_tmpl)) : 0) +
(m->xuo ? nla_total_size(sizeof(struct xfrm_user_offload)) : 0) +
(m->new_mark ? nla_total_size(sizeof(struct xfrm_mark)) : 0) +
((m->smark.v | m->smark.m) ? nla_total_size(sizeof(u32)) * 2 : 0) +
(m->mapping_maxage ? nla_total_size(sizeof(u32)) : 0) +
(m->nat_keepalive_interval ? nla_total_size(sizeof(u32)) : 0) +
(dir ? nla_total_size(sizeof(u8)) : 0); /* XFRMA_SA_DIR */
}
static int xfrm_send_migrate_state(struct net *net,
const struct xfrm_user_migrate_state *um,
const struct xfrm_migrate *m,
u8 dir, u32 portid, u32 seq)
{
int err;
struct sk_buff *skb;
skb = nlmsg_new(xfrm_migrate_state_msgsize(m, dir), GFP_ATOMIC);
if (!skb)
return -ENOMEM;
err = build_migrate_state(skb, um, m, dir, portid, seq);
if (err < 0) {
kfree_skb(skb);
return err;
}
return xfrm_nlmsg_multicast(net, skb, 0, XFRMNLGRP_MIGRATE);
}
static int xfrm_do_migrate_state(struct sk_buff *skb, struct nlmsghdr *nlh,
struct nlattr **attrs, struct netlink_ext_ack *extack)
{
struct xfrm_user_migrate_state *um = nlmsg_data(nlh);
struct net *net = sock_net(skb->sk);
struct xfrm_user_offload xuo = {};
struct xfrm_migrate m = {};
struct xfrm_state *xc;
struct xfrm_state *x;
int err;
if (!um->id.spi) {
NL_SET_ERR_MSG(extack, "Invalid SPI 0x0");
return -EINVAL;
}
if (um->reserved) {
NL_SET_ERR_MSG(extack, "Reserved field must be zero");
return -EINVAL;
}
if (um->flags & ~XFRM_MIGRATE_STATE_KNOWN_FLAGS) {
NL_SET_ERR_MSG_FMT(extack, "Unknown flags: 0x%x",
um->flags & ~XFRM_MIGRATE_STATE_KNOWN_FLAGS);
return -EINVAL;
}
err = verify_xfrm_family(um->new_family, extack);
if (err)
return err;
if (!(um->flags & XFRM_MIGRATE_STATE_UPDATE_H2H_SEL)) {
err = verify_selector_prefixlen(um->new_sel.family,
&um->new_sel, extack);
if (err)
return err;
}
copy_from_user_migrate_state(&m, um);
x = xfrm_state_lookup(net, m.old_mark.v & m.old_mark.m,
&um->id.daddr, um->id.spi,
um->id.proto, um->id.family);
if (!x) {
NL_SET_ERR_MSG(extack, "Can not find state");
return -ESRCH;
}
if (um->flags & XFRM_MIGRATE_STATE_UPDATE_H2H_SEL) {
u8 prefixlen = (x->props.family == AF_INET6) ? 128 : 32;
if (x->sel.prefixlen_s != x->sel.prefixlen_d ||
x->sel.prefixlen_d != prefixlen ||
!xfrm_addr_equal(&x->sel.daddr, &x->id.daddr, x->props.family) ||
!xfrm_addr_equal(&x->sel.saddr, &x->props.saddr, x->props.family)) {
NL_SET_ERR_MSG(extack,
"SA selector is not a single-host match for SA addresses");
err = -EINVAL;
goto out;
}
}
if (attrs[XFRMA_ENCAP]) {
m.encap = nla_data(attrs[XFRMA_ENCAP]);
if (m.encap->encap_type == 0) {
m.encap = NULL; /* sentinel: remove encap */
} else if (m.encap->encap_type != UDP_ENCAP_ESPINUDP) {
NL_SET_ERR_MSG(extack, "Unsupported encapsulation type");
err = -EINVAL;
goto out;
}
} else {
m.encap = x->encap; /* omit-to-inherit */
}
if (attrs[XFRMA_MTIMER_THRESH]) {
err = verify_mtimer_thresh(!!m.encap, x->dir, extack);
if (err)
goto out;
}
if (nla_get_u32_default(attrs[XFRMA_NAT_KEEPALIVE_INTERVAL], 0) && !m.encap) {
NL_SET_ERR_MSG(extack,
"NAT_KEEPALIVE_INTERVAL requires encapsulation");
err = -EINVAL;
goto out;
}
if (attrs[XFRMA_OFFLOAD_DEV]) {
m.xuo = nla_data(attrs[XFRMA_OFFLOAD_DEV]);
} else {
bool inherit_offload = !(um->flags & XFRM_MIGRATE_STATE_CLEAR_OFFLOAD);
if (inherit_offload && x->xso.dev) {
xso_to_xuo(&x->xso, &xuo);
m.xuo = &xuo;
}
}
if (attrs[XFRMA_MARK])
m.new_mark = nla_data(attrs[XFRMA_MARK]);
if (attrs[XFRMA_SET_MARK])
xfrm_smark_init(attrs, &m.smark);
else
m.smark = x->props.smark;
m.mapping_maxage = nla_get_u32_default(attrs[XFRMA_MTIMER_THRESH],
x->mapping_maxage);
m.nat_keepalive_interval = nla_get_u32_default(attrs[XFRMA_NAT_KEEPALIVE_INTERVAL],
x->nat_keepalive_interval);
if (m.new_family != um->id.family ||
!xfrm_addr_equal(&m.new_daddr, &um->id.daddr, um->id.family)) {
u32 new_mark_key = m.new_mark ? m.new_mark->v & m.new_mark->m :
m.old_mark.v & m.old_mark.m;
struct xfrm_state *x_new;
x_new = xfrm_state_lookup(net, new_mark_key, &m.new_daddr,
um->id.spi, um->id.proto, m.new_family);
if (x_new) {
xfrm_state_put(x_new);
NL_SET_ERR_MSG(extack, "New SA tuple already occupied");
err = -EEXIST;
goto out;
}
}
xc = xfrm_state_migrate_create(x, &m, net, extack);
if (!xc) {
NL_SET_ERR_MSG_WEAK(extack, "State migration clone failed");
err = -EINVAL;
goto out;
}
spin_lock_bh(&x->lock);
if (x->km.state != XFRM_STATE_VALID) {
spin_unlock_bh(&x->lock);
NL_SET_ERR_MSG(extack, "State already deleted");
err = -ESRCH;
goto out_xc;
}
xfrm_migrate_sync(xc, x); /* to prevent SN/IV reuse */
__xfrm_state_delete(x);
spin_unlock_bh(&x->lock);
err = xfrm_state_migrate_install(x, xc, &m, extack);
if (err < 0) {
/*
* Should not occur: pre-check above ensures the new tuple is
* free under xfrm_cfg_mutex. Both SAs are gone if it does;
* restoring x would risk SN/IV reuse.
*/
goto out;
}
/* Restore encap cleared by sentinel (type=0) during migration. */
if (attrs[XFRMA_ENCAP])
m.encap = nla_data(attrs[XFRMA_ENCAP]);
m.new_sel = &xc->sel;
m.mapping_maxage = xc->mapping_maxage;
m.nat_keepalive_interval = xc->nat_keepalive_interval;
err = xfrm_send_migrate_state(net, um, &m, xc->dir,
nlh->nlmsg_pid, nlh->nlmsg_seq);
if (err < 0) {
NL_SET_ERR_MSG(extack, "Failed to send migration notification");
err = 0;
}
out:
xfrm_state_put(x);
return err;
out_xc:
xc->km.state = XFRM_STATE_DEAD;
xfrm_state_put(xc);
xfrm_state_put(x);
return err;
}
#else
static int xfrm_do_migrate_state(struct sk_buff *skb, struct nlmsghdr *nlh,
struct nlattr **attrs, struct netlink_ext_ack *extack)
{
NL_SET_ERR_MSG(extack, "XFRM_MSG_MIGRATE_STATE is not supported");
return -ENOPROTOOPT;
}
static int xfrm_do_migrate(struct sk_buff *skb, struct nlmsghdr *nlh,
struct nlattr **attrs, struct netlink_ext_ack *extack)
{
@ -3345,6 +3662,7 @@ const int xfrm_msg_min[XFRM_NR_MSGTYPES] = {
[XFRM_MSG_GETSPDINFO - XFRM_MSG_BASE] = sizeof(u32),
[XFRM_MSG_SETDEFAULT - XFRM_MSG_BASE] = XMSGSIZE(xfrm_userpolicy_default),
[XFRM_MSG_GETDEFAULT - XFRM_MSG_BASE] = XMSGSIZE(xfrm_userpolicy_default),
[XFRM_MSG_MIGRATE_STATE - XFRM_MSG_BASE] = XMSGSIZE(xfrm_user_migrate_state),
};
EXPORT_SYMBOL_GPL(xfrm_msg_min);
@ -3438,6 +3756,7 @@ static const struct xfrm_link {
[XFRM_MSG_GETSPDINFO - XFRM_MSG_BASE] = { .doit = xfrm_get_spdinfo },
[XFRM_MSG_SETDEFAULT - XFRM_MSG_BASE] = { .doit = xfrm_set_default },
[XFRM_MSG_GETDEFAULT - XFRM_MSG_BASE] = { .doit = xfrm_get_default },
[XFRM_MSG_MIGRATE_STATE - XFRM_MSG_BASE] = { .doit = xfrm_do_migrate_state },
};
static int xfrm_reject_unused_attr(int type, struct nlattr **attrs,

View File

@ -128,6 +128,7 @@ static const struct nlmsg_perm nlmsg_xfrm_perms[] = {
{ XFRM_MSG_MAPPING, NETLINK_XFRM_SOCKET__NLMSG_READ },
{ XFRM_MSG_SETDEFAULT, NETLINK_XFRM_SOCKET__NLMSG_WRITE },
{ XFRM_MSG_GETDEFAULT, NETLINK_XFRM_SOCKET__NLMSG_READ },
{ XFRM_MSG_MIGRATE_STATE, NETLINK_XFRM_SOCKET__NLMSG_WRITE },
};
static const struct nlmsg_perm nlmsg_audit_perms[] = {
@ -203,7 +204,7 @@ int selinux_nlmsg_lookup(u16 sclass, u16 nlmsg_type, u32 *perm)
* structures at the top of this file with the new mappings
* before updating the BUILD_BUG_ON() macro!
*/
BUILD_BUG_ON(XFRM_MSG_MAX != XFRM_MSG_GETDEFAULT);
BUILD_BUG_ON(XFRM_MSG_MAX != XFRM_MSG_MIGRATE_STATE);
if (selinux_policycap_netlink_xperm()) {
*perm = NETLINK_XFRM_SOCKET__NLMSG;