Commit 08ec9af1 authored by David S. Miller's avatar David S. Miller

xfrm: Fix xfrm_state_find() wrt. wildcard source address.

The change to make xfrm_state objects hash on source address
broke the case where such source addresses are wildcarded.

Fix this by doing a two phase lookup, first with fully specified
source address, next using saddr wildcarded.
Reported-by: default avatarNicolas Dichtel <nicolas.dichtel@dev.6wind.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 9616a755
...@@ -748,12 +748,51 @@ static void xfrm_hash_grow_check(struct net *net, int have_hash_collision) ...@@ -748,12 +748,51 @@ static void xfrm_hash_grow_check(struct net *net, int have_hash_collision)
schedule_work(&net->xfrm.state_hash_work); schedule_work(&net->xfrm.state_hash_work);
} }
static void xfrm_state_look_at(struct xfrm_policy *pol, struct xfrm_state *x,
struct flowi *fl, unsigned short family,
xfrm_address_t *daddr, xfrm_address_t *saddr,
struct xfrm_state **best, int *acq_in_progress,
int *error)
{
/* Resolution logic:
* 1. There is a valid state with matching selector. Done.
* 2. Valid state with inappropriate selector. Skip.
*
* Entering area of "sysdeps".
*
* 3. If state is not valid, selector is temporary, it selects
* only session which triggered previous resolution. Key
* manager will do something to install a state with proper
* selector.
*/
if (x->km.state == XFRM_STATE_VALID) {
if ((x->sel.family &&
!xfrm_selector_match(&x->sel, fl, x->sel.family)) ||
!security_xfrm_state_pol_flow_match(x, pol, fl))
return;
if (!*best ||
(*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;
} else if (x->km.state == XFRM_STATE_ERROR ||
x->km.state == XFRM_STATE_EXPIRED) {
if (xfrm_selector_match(&x->sel, fl, x->sel.family) &&
security_xfrm_state_pol_flow_match(x, pol, fl))
*error = -ESRCH;
}
}
struct xfrm_state * struct xfrm_state *
xfrm_state_find(xfrm_address_t *daddr, xfrm_address_t *saddr, xfrm_state_find(xfrm_address_t *daddr, xfrm_address_t *saddr,
struct flowi *fl, struct xfrm_tmpl *tmpl, struct flowi *fl, struct xfrm_tmpl *tmpl,
struct xfrm_policy *pol, int *err, struct xfrm_policy *pol, int *err,
unsigned short family) unsigned short family)
{ {
static xfrm_address_t saddr_wildcard = { };
struct net *net = xp_net(pol); struct net *net = xp_net(pol);
unsigned int h; unsigned int h;
struct hlist_node *entry; struct hlist_node *entry;
...@@ -773,40 +812,27 @@ xfrm_state_find(xfrm_address_t *daddr, xfrm_address_t *saddr, ...@@ -773,40 +812,27 @@ xfrm_state_find(xfrm_address_t *daddr, xfrm_address_t *saddr,
xfrm_state_addr_check(x, daddr, saddr, family) && xfrm_state_addr_check(x, daddr, saddr, family) &&
tmpl->mode == x->props.mode && tmpl->mode == x->props.mode &&
tmpl->id.proto == x->id.proto && tmpl->id.proto == x->id.proto &&
(tmpl->id.spi == x->id.spi || !tmpl->id.spi)) { (tmpl->id.spi == x->id.spi || !tmpl->id.spi))
/* Resolution logic: xfrm_state_look_at(pol, x, fl, family, daddr, saddr,
1. There is a valid state with matching selector. &best, &acquire_in_progress, &error);
Done. }
2. Valid state with inappropriate selector. Skip. if (best)
goto found;
Entering area of "sysdeps".
h = xfrm_dst_hash(net, daddr, &saddr_wildcard, tmpl->reqid, family);
3. If state is not valid, selector is temporary, hlist_for_each_entry(x, entry, net->xfrm.state_bydst+h, bydst) {
it selects only session which triggered if (x->props.family == family &&
previous resolution. Key manager will do x->props.reqid == tmpl->reqid &&
something to install a state with proper !(x->props.flags & XFRM_STATE_WILDRECV) &&
selector. xfrm_state_addr_check(x, daddr, saddr, family) &&
*/ tmpl->mode == x->props.mode &&
if (x->km.state == XFRM_STATE_VALID) { tmpl->id.proto == x->id.proto &&
if ((x->sel.family && !xfrm_selector_match(&x->sel, fl, x->sel.family)) || (tmpl->id.spi == x->id.spi || !tmpl->id.spi))
!security_xfrm_state_pol_flow_match(x, pol, fl)) xfrm_state_look_at(pol, x, fl, family, daddr, saddr,
continue; &best, &acquire_in_progress, &error);
if (!best ||
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) {
acquire_in_progress = 1;
} else if (x->km.state == XFRM_STATE_ERROR ||
x->km.state == XFRM_STATE_EXPIRED) {
if (xfrm_selector_match(&x->sel, fl, x->sel.family) &&
security_xfrm_state_pol_flow_match(x, pol, fl))
error = -ESRCH;
}
}
} }
found:
x = best; x = best;
if (!x && !error && !acquire_in_progress) { if (!x && !error && !acquire_in_progress) {
if (tmpl->id.spi && if (tmpl->id.spi &&
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment