Commit a1e59abf authored by Patrick McHardy's avatar Patrick McHardy Committed by David S. Miller
Browse files

[XFRM]: Fix wildcard as tunnel source



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: default avatarPatrick McHardy <kaber@trash.net>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 1ef9696c
Loading
Loading
Loading
Loading
+13 −0
Original line number Diff line number Diff line
@@ -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, 
@@ -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)
{
+20 −0
Original line number Diff line number Diff line
@@ -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)
{
@@ -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,
+0 −15
Original line number Diff line number Diff line
@@ -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;
+21 −0
Original line number Diff line number Diff line
@@ -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)
{
@@ -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,
+0 −16
Original line number Diff line number Diff line
@@ -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;
Loading