Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/klassert/ipsec...
[cascardo/linux.git] / net / ipv6 / ip6_vti.c
index bcda14d..ace10d0 100644 (file)
@@ -95,6 +95,7 @@ vti6_tnl_lookup(struct net *net, const struct in6_addr *remote,
        unsigned int hash = HASH(remote, local);
        struct ip6_tnl *t;
        struct vti6_net *ip6n = net_generic(net, vti6_net_id);
+       struct in6_addr any;
 
        for_each_vti6_tunnel_rcu(ip6n->tnls_r_l[hash]) {
                if (ipv6_addr_equal(local, &t->parms.laddr) &&
@@ -102,6 +103,22 @@ vti6_tnl_lookup(struct net *net, const struct in6_addr *remote,
                    (t->dev->flags & IFF_UP))
                        return t;
        }
+
+       memset(&any, 0, sizeof(any));
+       hash = HASH(&any, local);
+       for_each_vti6_tunnel_rcu(ip6n->tnls_r_l[hash]) {
+               if (ipv6_addr_equal(local, &t->parms.laddr) &&
+                   (t->dev->flags & IFF_UP))
+                       return t;
+       }
+
+       hash = HASH(remote, &any);
+       for_each_vti6_tunnel_rcu(ip6n->tnls_r_l[hash]) {
+               if (ipv6_addr_equal(remote, &t->parms.raddr) &&
+                   (t->dev->flags & IFF_UP))
+                       return t;
+       }
+
        t = rcu_dereference(ip6n->tnls_wc[0]);
        if (t && (t->dev->flags & IFF_UP))
                return t;
@@ -287,8 +304,8 @@ static int vti6_rcv(struct sk_buff *skb)
        const struct ipv6hdr *ipv6h = ipv6_hdr(skb);
 
        rcu_read_lock();
-       if ((t = vti6_tnl_lookup(dev_net(skb->dev), &ipv6h->saddr,
-                                &ipv6h->daddr)) != NULL) {
+       t = vti6_tnl_lookup(dev_net(skb->dev), &ipv6h->saddr, &ipv6h->daddr);
+       if (t != NULL) {
                if (t->parms.proto != IPPROTO_IPV6 && t->parms.proto != 0) {
                        rcu_read_unlock();
                        goto discard;
@@ -412,6 +429,7 @@ vti6_xmit(struct sk_buff *skb, struct net_device *dev, struct flowi *fl)
        struct net_device_stats *stats = &t->dev->stats;
        struct dst_entry *dst = skb_dst(skb);
        struct net_device *tdev;
+       struct xfrm_state *x;
        int err = -1;
 
        if (!dst)
@@ -425,7 +443,12 @@ vti6_xmit(struct sk_buff *skb, struct net_device *dev, struct flowi *fl)
                goto tx_err_link_failure;
        }
 
-       if (!vti6_state_check(dst->xfrm, &t->parms.raddr, &t->parms.laddr))
+       x = dst->xfrm;
+       if (!vti6_state_check(x, &t->parms.raddr, &t->parms.laddr))
+               goto tx_err_link_failure;
+
+       if (!ip6_tnl_xmit_ctl(t, (const struct in6_addr *)&x->props.saddr,
+                             (const struct in6_addr *)&x->id.daddr))
                goto tx_err_link_failure;
 
        tdev = dst->dev;
@@ -480,7 +503,7 @@ vti6_tnl_xmit(struct sk_buff *skb, struct net_device *dev)
                ipv6h = ipv6_hdr(skb);
 
                if ((t->parms.proto != IPPROTO_IPV6 && t->parms.proto != 0) ||
-                   !ip6_tnl_xmit_ctl(t) || vti6_addr_conflict(t, ipv6h))
+                   vti6_addr_conflict(t, ipv6h))
                        goto tx_err;
 
                xfrm_decode_session(skb, &fl, AF_INET6);