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, 
 #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)
 {
 
        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)
 {
        .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,
 
        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;
 
        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)
 {
        .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,
 
        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;
 
        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
        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;
                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);