mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-09-30 22:26:55 +00:00
xfrm: Check if_id in xfrm_migrate
[ Upstream commit c1aca3080e
]
This patch enables distinguishing SAs and SPs based on if_id during
the xfrm_migrate flow. This ensures support for xfrm interfaces
throughout the SA/SP lifecycle.
When there are multiple existing SPs with the same direction,
the same xfrm_selector and different endpoint addresses,
xfrm_migrate might fail with ENODATA.
Specifically, the code path for performing xfrm_migrate is:
Stage 1: find policy to migrate with
xfrm_migrate_policy_find(sel, dir, type, net)
Stage 2: find and update state(s) with
xfrm_migrate_state_find(mp, net)
Stage 3: update endpoint address(es) of template(s) with
xfrm_policy_migrate(pol, m, num_migrate)
Currently "Stage 1" always returns the first xfrm_policy that
matches, and "Stage 3" looks for the xfrm_tmpl that matches the
old endpoint address. Thus if there are multiple xfrm_policy
with same selector, direction, type and net, "Stage 1" might
rertun a wrong xfrm_policy and "Stage 3" will fail with ENODATA
because it cannot find a xfrm_tmpl with the matching endpoint
address.
The fix is to allow userspace to pass an if_id and add if_id
to the matching rule in Stage 1 and Stage 2 since if_id is a
unique ID for xfrm_policy and xfrm_state. For compatibility,
if_id will only be checked if the attribute is set.
Tested with additions to Android's kernel unit test suite:
https://android-review.googlesource.com/c/kernel/tests/+/1668886
Signed-off-by: Yan Yan <evitayan@google.com>
Signed-off-by: Steffen Klassert <steffen.klassert@secunet.com>
Signed-off-by: Sasha Levin <sashal@kernel.org>
This commit is contained in:
parent
8918ae9741
commit
e6d7e51e10
5 changed files with 23 additions and 11 deletions
|
@ -1679,14 +1679,15 @@ int km_migrate(const struct xfrm_selector *sel, u8 dir, u8 type,
|
||||||
const struct xfrm_migrate *m, int num_bundles,
|
const struct xfrm_migrate *m, int num_bundles,
|
||||||
const struct xfrm_kmaddress *k,
|
const struct xfrm_kmaddress *k,
|
||||||
const struct xfrm_encap_tmpl *encap);
|
const struct xfrm_encap_tmpl *encap);
|
||||||
struct xfrm_state *xfrm_migrate_state_find(struct xfrm_migrate *m, struct net *net);
|
struct xfrm_state *xfrm_migrate_state_find(struct xfrm_migrate *m, struct net *net,
|
||||||
|
u32 if_id);
|
||||||
struct xfrm_state *xfrm_state_migrate(struct xfrm_state *x,
|
struct xfrm_state *xfrm_state_migrate(struct xfrm_state *x,
|
||||||
struct xfrm_migrate *m,
|
struct xfrm_migrate *m,
|
||||||
struct xfrm_encap_tmpl *encap);
|
struct xfrm_encap_tmpl *encap);
|
||||||
int xfrm_migrate(const struct xfrm_selector *sel, u8 dir, u8 type,
|
int xfrm_migrate(const struct xfrm_selector *sel, u8 dir, u8 type,
|
||||||
struct xfrm_migrate *m, int num_bundles,
|
struct xfrm_migrate *m, int num_bundles,
|
||||||
struct xfrm_kmaddress *k, struct net *net,
|
struct xfrm_kmaddress *k, struct net *net,
|
||||||
struct xfrm_encap_tmpl *encap);
|
struct xfrm_encap_tmpl *encap, u32 if_id);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
int km_new_mapping(struct xfrm_state *x, xfrm_address_t *ipaddr, __be16 sport);
|
int km_new_mapping(struct xfrm_state *x, xfrm_address_t *ipaddr, __be16 sport);
|
||||||
|
|
|
@ -2623,7 +2623,7 @@ static int pfkey_migrate(struct sock *sk, struct sk_buff *skb,
|
||||||
}
|
}
|
||||||
|
|
||||||
return xfrm_migrate(&sel, dir, XFRM_POLICY_TYPE_MAIN, m, i,
|
return xfrm_migrate(&sel, dir, XFRM_POLICY_TYPE_MAIN, m, i,
|
||||||
kma ? &k : NULL, net, NULL);
|
kma ? &k : NULL, net, NULL, 0);
|
||||||
|
|
||||||
out:
|
out:
|
||||||
return err;
|
return err;
|
||||||
|
|
|
@ -4259,7 +4259,7 @@ static bool xfrm_migrate_selector_match(const struct xfrm_selector *sel_cmp,
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct xfrm_policy *xfrm_migrate_policy_find(const struct xfrm_selector *sel,
|
static struct xfrm_policy *xfrm_migrate_policy_find(const struct xfrm_selector *sel,
|
||||||
u8 dir, u8 type, struct net *net)
|
u8 dir, u8 type, struct net *net, u32 if_id)
|
||||||
{
|
{
|
||||||
struct xfrm_policy *pol, *ret = NULL;
|
struct xfrm_policy *pol, *ret = NULL;
|
||||||
struct hlist_head *chain;
|
struct hlist_head *chain;
|
||||||
|
@ -4268,7 +4268,8 @@ static struct xfrm_policy *xfrm_migrate_policy_find(const struct xfrm_selector *
|
||||||
spin_lock_bh(&net->xfrm.xfrm_policy_lock);
|
spin_lock_bh(&net->xfrm.xfrm_policy_lock);
|
||||||
chain = policy_hash_direct(net, &sel->daddr, &sel->saddr, sel->family, dir);
|
chain = policy_hash_direct(net, &sel->daddr, &sel->saddr, sel->family, dir);
|
||||||
hlist_for_each_entry(pol, chain, bydst) {
|
hlist_for_each_entry(pol, chain, bydst) {
|
||||||
if (xfrm_migrate_selector_match(sel, &pol->selector) &&
|
if ((if_id == 0 || pol->if_id == if_id) &&
|
||||||
|
xfrm_migrate_selector_match(sel, &pol->selector) &&
|
||||||
pol->type == type) {
|
pol->type == type) {
|
||||||
ret = pol;
|
ret = pol;
|
||||||
priority = ret->priority;
|
priority = ret->priority;
|
||||||
|
@ -4280,7 +4281,8 @@ static struct xfrm_policy *xfrm_migrate_policy_find(const struct xfrm_selector *
|
||||||
if ((pol->priority >= priority) && ret)
|
if ((pol->priority >= priority) && ret)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
if (xfrm_migrate_selector_match(sel, &pol->selector) &&
|
if ((if_id == 0 || pol->if_id == if_id) &&
|
||||||
|
xfrm_migrate_selector_match(sel, &pol->selector) &&
|
||||||
pol->type == type) {
|
pol->type == type) {
|
||||||
ret = pol;
|
ret = pol;
|
||||||
break;
|
break;
|
||||||
|
@ -4396,7 +4398,7 @@ static int xfrm_migrate_check(const struct xfrm_migrate *m, int num_migrate)
|
||||||
int xfrm_migrate(const struct xfrm_selector *sel, u8 dir, u8 type,
|
int xfrm_migrate(const struct xfrm_selector *sel, u8 dir, u8 type,
|
||||||
struct xfrm_migrate *m, int num_migrate,
|
struct xfrm_migrate *m, int num_migrate,
|
||||||
struct xfrm_kmaddress *k, struct net *net,
|
struct xfrm_kmaddress *k, struct net *net,
|
||||||
struct xfrm_encap_tmpl *encap)
|
struct xfrm_encap_tmpl *encap, u32 if_id)
|
||||||
{
|
{
|
||||||
int i, err, nx_cur = 0, nx_new = 0;
|
int i, err, nx_cur = 0, nx_new = 0;
|
||||||
struct xfrm_policy *pol = NULL;
|
struct xfrm_policy *pol = NULL;
|
||||||
|
@ -4415,14 +4417,14 @@ int xfrm_migrate(const struct xfrm_selector *sel, u8 dir, u8 type,
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Stage 1 - find policy */
|
/* Stage 1 - find policy */
|
||||||
if ((pol = xfrm_migrate_policy_find(sel, dir, type, net)) == NULL) {
|
if ((pol = xfrm_migrate_policy_find(sel, dir, type, net, if_id)) == NULL) {
|
||||||
err = -ENOENT;
|
err = -ENOENT;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Stage 2 - find and update state(s) */
|
/* Stage 2 - find and update state(s) */
|
||||||
for (i = 0, mp = m; i < num_migrate; i++, mp++) {
|
for (i = 0, mp = m; i < num_migrate; i++, mp++) {
|
||||||
if ((x = xfrm_migrate_state_find(mp, net))) {
|
if ((x = xfrm_migrate_state_find(mp, net, if_id))) {
|
||||||
x_cur[nx_cur] = x;
|
x_cur[nx_cur] = x;
|
||||||
nx_cur++;
|
nx_cur++;
|
||||||
xc = xfrm_state_migrate(x, mp, encap);
|
xc = xfrm_state_migrate(x, mp, encap);
|
||||||
|
|
|
@ -1605,7 +1605,8 @@ static struct xfrm_state *xfrm_state_clone(struct xfrm_state *orig,
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct xfrm_state *xfrm_migrate_state_find(struct xfrm_migrate *m, struct net *net)
|
struct xfrm_state *xfrm_migrate_state_find(struct xfrm_migrate *m, struct net *net,
|
||||||
|
u32 if_id)
|
||||||
{
|
{
|
||||||
unsigned int h;
|
unsigned int h;
|
||||||
struct xfrm_state *x = NULL;
|
struct xfrm_state *x = NULL;
|
||||||
|
@ -1621,6 +1622,8 @@ struct xfrm_state *xfrm_migrate_state_find(struct xfrm_migrate *m, struct net *n
|
||||||
continue;
|
continue;
|
||||||
if (m->reqid && x->props.reqid != m->reqid)
|
if (m->reqid && x->props.reqid != m->reqid)
|
||||||
continue;
|
continue;
|
||||||
|
if (if_id != 0 && x->if_id != if_id)
|
||||||
|
continue;
|
||||||
if (!xfrm_addr_equal(&x->id.daddr, &m->old_daddr,
|
if (!xfrm_addr_equal(&x->id.daddr, &m->old_daddr,
|
||||||
m->old_family) ||
|
m->old_family) ||
|
||||||
!xfrm_addr_equal(&x->props.saddr, &m->old_saddr,
|
!xfrm_addr_equal(&x->props.saddr, &m->old_saddr,
|
||||||
|
@ -1636,6 +1639,8 @@ struct xfrm_state *xfrm_migrate_state_find(struct xfrm_migrate *m, struct net *n
|
||||||
if (x->props.mode != m->mode ||
|
if (x->props.mode != m->mode ||
|
||||||
x->id.proto != m->proto)
|
x->id.proto != m->proto)
|
||||||
continue;
|
continue;
|
||||||
|
if (if_id != 0 && x->if_id != if_id)
|
||||||
|
continue;
|
||||||
if (!xfrm_addr_equal(&x->id.daddr, &m->old_daddr,
|
if (!xfrm_addr_equal(&x->id.daddr, &m->old_daddr,
|
||||||
m->old_family) ||
|
m->old_family) ||
|
||||||
!xfrm_addr_equal(&x->props.saddr, &m->old_saddr,
|
!xfrm_addr_equal(&x->props.saddr, &m->old_saddr,
|
||||||
|
|
|
@ -2592,6 +2592,7 @@ static int xfrm_do_migrate(struct sk_buff *skb, struct nlmsghdr *nlh,
|
||||||
int n = 0;
|
int n = 0;
|
||||||
struct net *net = sock_net(skb->sk);
|
struct net *net = sock_net(skb->sk);
|
||||||
struct xfrm_encap_tmpl *encap = NULL;
|
struct xfrm_encap_tmpl *encap = NULL;
|
||||||
|
u32 if_id = 0;
|
||||||
|
|
||||||
if (attrs[XFRMA_MIGRATE] == NULL)
|
if (attrs[XFRMA_MIGRATE] == NULL)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
@ -2616,7 +2617,10 @@ static int xfrm_do_migrate(struct sk_buff *skb, struct nlmsghdr *nlh,
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
err = xfrm_migrate(&pi->sel, pi->dir, type, m, n, kmp, net, encap);
|
if (attrs[XFRMA_IF_ID])
|
||||||
|
if_id = nla_get_u32(attrs[XFRMA_IF_ID]);
|
||||||
|
|
||||||
|
err = xfrm_migrate(&pi->sel, pi->dir, type, m, n, kmp, net, encap, if_id);
|
||||||
|
|
||||||
kfree(encap);
|
kfree(encap);
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue