Skip to content

Commit

Permalink
[XFRM]: Fix wildcard as tunnel source
Browse files Browse the repository at this point in the history
Hashing SAs by source address breaks templates with wildcards as tunnel
source since the source address used for hashing/lookup is still 0/0.
Move source address lookup to xfrm_tmpl_resolve_one() so we can use the
real address in the lookup.

Signed-off-by: Patrick McHardy <kaber@trash.net>
Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
Patrick McHardy authored and David S. Miller committed Sep 22, 2006
1 parent 1ef9696 commit a1e59ab
Show file tree
Hide file tree
Showing 6 changed files with 75 additions and 31 deletions.
13 changes: 13 additions & 0 deletions include/net/xfrm.h
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,7 @@ struct xfrm_policy_afinfo {
struct dst_ops *dst_ops;
void (*garbage_collect)(void);
int (*dst_lookup)(struct xfrm_dst **dst, struct flowi *fl);
int (*get_saddr)(xfrm_address_t *saddr, xfrm_address_t *daddr);
struct dst_entry *(*find_bundle)(struct flowi *fl, struct xfrm_policy *policy);
int (*bundle_create)(struct xfrm_policy *policy,
struct xfrm_state **xfrm,
Expand Down Expand Up @@ -630,6 +631,18 @@ secpath_reset(struct sk_buff *skb)
#endif
}

static inline int
xfrm_addr_any(xfrm_address_t *addr, unsigned short family)
{
switch (family) {
case AF_INET:
return addr->a4 == 0;
case AF_INET6:
return ipv6_addr_any((struct in6_addr *)&addr->a6);
}
return 0;
}

static inline int
__xfrm4_state_addr_cmp(struct xfrm_tmpl *tmpl, struct xfrm_state *x)
{
Expand Down
20 changes: 20 additions & 0 deletions net/ipv4/xfrm4_policy.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,25 @@ static int xfrm4_dst_lookup(struct xfrm_dst **dst, struct flowi *fl)
return __ip_route_output_key((struct rtable**)dst, fl);
}

static int xfrm4_get_saddr(xfrm_address_t *saddr, xfrm_address_t *daddr)
{
struct rtable *rt;
struct flowi fl_tunnel = {
.nl_u = {
.ip4_u = {
.daddr = daddr->a4,
},
},
};

if (!xfrm4_dst_lookup((struct xfrm_dst **)&rt, &fl_tunnel)) {
saddr->a4 = rt->rt_src;
dst_release(&rt->u.dst);
return 0;
}
return -EHOSTUNREACH;
}

static struct dst_entry *
__xfrm4_find_bundle(struct flowi *fl, struct xfrm_policy *policy)
{
Expand Down Expand Up @@ -298,6 +317,7 @@ static struct xfrm_policy_afinfo xfrm4_policy_afinfo = {
.family = AF_INET,
.dst_ops = &xfrm4_dst_ops,
.dst_lookup = xfrm4_dst_lookup,
.get_saddr = xfrm4_get_saddr,
.find_bundle = __xfrm4_find_bundle,
.bundle_create = __xfrm4_bundle_create,
.decode_session = _decode_session4,
Expand Down
15 changes: 0 additions & 15 deletions net/ipv4/xfrm4_state.c
Original file line number Diff line number Diff line change
Expand Up @@ -42,21 +42,6 @@ __xfrm4_init_tempsel(struct xfrm_state *x, struct flowi *fl,
x->props.saddr = tmpl->saddr;
if (x->props.saddr.a4 == 0)
x->props.saddr.a4 = saddr->a4;
if (tmpl->mode == XFRM_MODE_TUNNEL && x->props.saddr.a4 == 0) {
struct rtable *rt;
struct flowi fl_tunnel = {
.nl_u = {
.ip4_u = {
.daddr = x->id.daddr.a4,
}
}
};
if (!xfrm_dst_lookup((struct xfrm_dst **)&rt,
&fl_tunnel, AF_INET)) {
x->props.saddr.a4 = rt->rt_src;
dst_release(&rt->u.dst);
}
}
x->props.mode = tmpl->mode;
x->props.reqid = tmpl->reqid;
x->props.family = AF_INET;
Expand Down
21 changes: 21 additions & 0 deletions net/ipv6/xfrm6_policy.c
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,26 @@ static int xfrm6_dst_lookup(struct xfrm_dst **dst, struct flowi *fl)
return err;
}

static int xfrm6_get_saddr(xfrm_address_t *saddr, xfrm_address_t *daddr)
{
struct rt6_info *rt;
struct flowi fl_tunnel = {
.nl_u = {
.ip6_u = {
.daddr = *(struct in6_addr *)&daddr->a6,
},
},
};

if (!xfrm6_dst_lookup((struct xfrm_dst **)&rt, &fl_tunnel)) {
ipv6_get_saddr(&rt->u.dst, (struct in6_addr *)&daddr->a6,
(struct in6_addr *)&saddr->a6);
dst_release(&rt->u.dst);
return 0;
}
return -EHOSTUNREACH;
}

static struct dst_entry *
__xfrm6_find_bundle(struct flowi *fl, struct xfrm_policy *policy)
{
Expand Down Expand Up @@ -362,6 +382,7 @@ static struct xfrm_policy_afinfo xfrm6_policy_afinfo = {
.family = AF_INET6,
.dst_ops = &xfrm6_dst_ops,
.dst_lookup = xfrm6_dst_lookup,
.get_saddr = xfrm6_get_saddr,
.find_bundle = __xfrm6_find_bundle,
.bundle_create = __xfrm6_bundle_create,
.decode_session = _decode_session6,
Expand Down
16 changes: 0 additions & 16 deletions net/ipv6/xfrm6_state.c
Original file line number Diff line number Diff line change
Expand Up @@ -42,22 +42,6 @@ __xfrm6_init_tempsel(struct xfrm_state *x, struct flowi *fl,
memcpy(&x->props.saddr, &tmpl->saddr, sizeof(x->props.saddr));
if (ipv6_addr_any((struct in6_addr*)&x->props.saddr))
memcpy(&x->props.saddr, saddr, sizeof(x->props.saddr));
if (tmpl->mode == XFRM_MODE_TUNNEL && ipv6_addr_any((struct in6_addr*)&x->props.saddr)) {
struct rt6_info *rt;
struct flowi fl_tunnel = {
.nl_u = {
.ip6_u = {
.daddr = *(struct in6_addr *)daddr,
}
}
};
if (!xfrm_dst_lookup((struct xfrm_dst **)&rt,
&fl_tunnel, AF_INET6)) {
ipv6_get_saddr(&rt->u.dst, (struct in6_addr *)daddr,
(struct in6_addr *)&x->props.saddr);
dst_release(&rt->u.dst);
}
}
x->props.mode = tmpl->mode;
x->props.reqid = tmpl->reqid;
x->props.family = AF_INET6;
Expand Down
21 changes: 21 additions & 0 deletions net/xfrm/xfrm_policy.c
Original file line number Diff line number Diff line change
Expand Up @@ -1107,6 +1107,20 @@ int __xfrm_sk_clone_policy(struct sock *sk)
return 0;
}

static int
xfrm_get_saddr(xfrm_address_t *local, xfrm_address_t *remote,
unsigned short family)
{
int err;
struct xfrm_policy_afinfo *afinfo = xfrm_policy_get_afinfo(family);

if (unlikely(afinfo == NULL))
return -EINVAL;
err = afinfo->get_saddr(local, remote);
xfrm_policy_put_afinfo(afinfo);
return err;
}

/* Resolve list of templates for the flow, given policy. */

static int
Expand All @@ -1118,6 +1132,7 @@ xfrm_tmpl_resolve_one(struct xfrm_policy *policy, struct flowi *fl,
int i, error;
xfrm_address_t *daddr = xfrm_flowi_daddr(fl, family);
xfrm_address_t *saddr = xfrm_flowi_saddr(fl, family);
xfrm_address_t tmp;

for (nx=0, i = 0; i < policy->xfrm_nr; i++) {
struct xfrm_state *x;
Expand All @@ -1128,6 +1143,12 @@ xfrm_tmpl_resolve_one(struct xfrm_policy *policy, struct flowi *fl,
if (tmpl->mode == XFRM_MODE_TUNNEL) {
remote = &tmpl->id.daddr;
local = &tmpl->saddr;
if (xfrm_addr_any(local, family)) {
error = xfrm_get_saddr(&tmp, remote, family);
if (error)
goto fail;
local = &tmp;
}
}

x = xfrm_state_find(remote, local, fl, tmpl, policy, &error, family);
Expand Down

0 comments on commit a1e59ab

Please sign in to comment.