mirror of
https://github.com/torvalds/linux.git
synced 2024-11-22 12:11:40 +00:00
xfrm: Check if_id in xfrm_migrate
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>
This commit is contained in:
parent
6596a02295
commit
c1aca3080e
@ -1681,14 +1681,15 @@ int km_migrate(const struct xfrm_selector *sel, u8 dir, u8 type,
|
||||
const struct xfrm_migrate *m, int num_bundles,
|
||||
const struct xfrm_kmaddress *k,
|
||||
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_migrate *m,
|
||||
struct xfrm_encap_tmpl *encap);
|
||||
int xfrm_migrate(const struct xfrm_selector *sel, u8 dir, u8 type,
|
||||
struct xfrm_migrate *m, int num_bundles,
|
||||
struct xfrm_kmaddress *k, struct net *net,
|
||||
struct xfrm_encap_tmpl *encap);
|
||||
struct xfrm_encap_tmpl *encap, u32 if_id);
|
||||
#endif
|
||||
|
||||
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,
|
||||
kma ? &k : NULL, net, NULL);
|
||||
kma ? &k : NULL, net, NULL, 0);
|
||||
|
||||
out:
|
||||
return err;
|
||||
|
@ -4256,7 +4256,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,
|
||||
u8 dir, u8 type, struct net *net)
|
||||
u8 dir, u8 type, struct net *net, u32 if_id)
|
||||
{
|
||||
struct xfrm_policy *pol, *ret = NULL;
|
||||
struct hlist_head *chain;
|
||||
@ -4265,7 +4265,8 @@ static struct xfrm_policy *xfrm_migrate_policy_find(const struct xfrm_selector *
|
||||
spin_lock_bh(&net->xfrm.xfrm_policy_lock);
|
||||
chain = policy_hash_direct(net, &sel->daddr, &sel->saddr, sel->family, dir);
|
||||
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) {
|
||||
ret = pol;
|
||||
priority = ret->priority;
|
||||
@ -4277,7 +4278,8 @@ static struct xfrm_policy *xfrm_migrate_policy_find(const struct xfrm_selector *
|
||||
if ((pol->priority >= priority) && ret)
|
||||
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) {
|
||||
ret = pol;
|
||||
break;
|
||||
@ -4393,7 +4395,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,
|
||||
struct xfrm_migrate *m, int num_migrate,
|
||||
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;
|
||||
struct xfrm_policy *pol = NULL;
|
||||
@ -4412,14 +4414,14 @@ int xfrm_migrate(const struct xfrm_selector *sel, u8 dir, u8 type,
|
||||
}
|
||||
|
||||
/* 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;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Stage 2 - find and update state(s) */
|
||||
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;
|
||||
nx_cur++;
|
||||
xc = xfrm_state_migrate(x, mp, encap);
|
||||
|
@ -1606,7 +1606,8 @@ out:
|
||||
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;
|
||||
struct xfrm_state *x = NULL;
|
||||
@ -1622,6 +1623,8 @@ struct xfrm_state *xfrm_migrate_state_find(struct xfrm_migrate *m, struct net *n
|
||||
continue;
|
||||
if (m->reqid && x->props.reqid != m->reqid)
|
||||
continue;
|
||||
if (if_id != 0 && x->if_id != if_id)
|
||||
continue;
|
||||
if (!xfrm_addr_equal(&x->id.daddr, &m->old_daddr,
|
||||
m->old_family) ||
|
||||
!xfrm_addr_equal(&x->props.saddr, &m->old_saddr,
|
||||
@ -1637,6 +1640,8 @@ struct xfrm_state *xfrm_migrate_state_find(struct xfrm_migrate *m, struct net *n
|
||||
if (x->props.mode != m->mode ||
|
||||
x->id.proto != m->proto)
|
||||
continue;
|
||||
if (if_id != 0 && x->if_id != if_id)
|
||||
continue;
|
||||
if (!xfrm_addr_equal(&x->id.daddr, &m->old_daddr,
|
||||
m->old_family) ||
|
||||
!xfrm_addr_equal(&x->props.saddr, &m->old_saddr,
|
||||
|
@ -2608,6 +2608,7 @@ static int xfrm_do_migrate(struct sk_buff *skb, struct nlmsghdr *nlh,
|
||||
int n = 0;
|
||||
struct net *net = sock_net(skb->sk);
|
||||
struct xfrm_encap_tmpl *encap = NULL;
|
||||
u32 if_id = 0;
|
||||
|
||||
if (attrs[XFRMA_MIGRATE] == NULL)
|
||||
return -EINVAL;
|
||||
@ -2632,7 +2633,10 @@ static int xfrm_do_migrate(struct sk_buff *skb, struct nlmsghdr *nlh,
|
||||
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);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user