--- /dev/null
+From 199ab00f3cdb6f154ea93fa76fd80192861a821d Mon Sep 17 00:00:00 2001
+From: WANG Cong <xiyou.wangcong@gmail.com>
+Date: Tue, 25 Apr 2017 14:37:15 -0700
+Subject: ipv6: check skb->protocol before lookup for nexthop
+
+From: WANG Cong <xiyou.wangcong@gmail.com>
+
+commit 199ab00f3cdb6f154ea93fa76fd80192861a821d upstream.
+
+Andrey reported a out-of-bound access in ip6_tnl_xmit(), this
+is because we use an ipv4 dst in ip6_tnl_xmit() and cast an IPv4
+neigh key as an IPv6 address:
+
+ neigh = dst_neigh_lookup(skb_dst(skb),
+ &ipv6_hdr(skb)->daddr);
+ if (!neigh)
+ goto tx_err_link_failure;
+
+ addr6 = (struct in6_addr *)&neigh->primary_key; // <=== HERE
+ addr_type = ipv6_addr_type(addr6);
+
+ if (addr_type == IPV6_ADDR_ANY)
+ addr6 = &ipv6_hdr(skb)->daddr;
+
+ memcpy(&fl6->daddr, addr6, sizeof(fl6->daddr));
+
+Also the network header of the skb at this point should be still IPv4
+for 4in6 tunnels, we shold not just use it as IPv6 header.
+
+This patch fixes it by checking if skb->protocol is ETH_P_IPV6: if it
+is, we are safe to do the nexthop lookup using skb_dst() and
+ipv6_hdr(skb)->daddr; if not (aka IPv4), we have no clue about which
+dest address we can pick here, we have to rely on callers to fill it
+from tunnel config, so just fall to ip6_route_output() to make the
+decision.
+
+Fixes: ea3dc9601bda ("ip6_tunnel: Add support for wildcard tunnel endpoints.")
+Reported-by: Andrey Konovalov <andreyknvl@google.com>
+Tested-by: Andrey Konovalov <andreyknvl@google.com>
+Cc: Steffen Klassert <steffen.klassert@secunet.com>
+Signed-off-by: Cong Wang <xiyou.wangcong@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Signed-off-by: Alessio Balsini <balsini@android.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ net/ipv6/ip6_tunnel.c | 42 ++++++++++++++++++++++--------------------
+ 1 file changed, 22 insertions(+), 20 deletions(-)
+
+--- a/net/ipv6/ip6_tunnel.c
++++ b/net/ipv6/ip6_tunnel.c
+@@ -972,26 +972,28 @@ static int ip6_tnl_xmit2(struct sk_buff
+
+ /* NBMA tunnel */
+ if (ipv6_addr_any(&t->parms.raddr)) {
+- struct in6_addr *addr6;
+- struct neighbour *neigh;
+- int addr_type;
+-
+- if (!skb_dst(skb))
+- goto tx_err_link_failure;
+-
+- neigh = dst_neigh_lookup(skb_dst(skb),
+- &ipv6_hdr(skb)->daddr);
+- if (!neigh)
+- goto tx_err_link_failure;
+-
+- addr6 = (struct in6_addr *)&neigh->primary_key;
+- addr_type = ipv6_addr_type(addr6);
+-
+- if (addr_type == IPV6_ADDR_ANY)
+- addr6 = &ipv6_hdr(skb)->daddr;
+-
+- memcpy(&fl6->daddr, addr6, sizeof(fl6->daddr));
+- neigh_release(neigh);
++ if (skb->protocol == htons(ETH_P_IPV6)) {
++ struct in6_addr *addr6;
++ struct neighbour *neigh;
++ int addr_type;
++
++ if (!skb_dst(skb))
++ goto tx_err_link_failure;
++
++ neigh = dst_neigh_lookup(skb_dst(skb),
++ &ipv6_hdr(skb)->daddr);
++ if (!neigh)
++ goto tx_err_link_failure;
++
++ addr6 = (struct in6_addr *)&neigh->primary_key;
++ addr_type = ipv6_addr_type(addr6);
++
++ if (addr_type == IPV6_ADDR_ANY)
++ addr6 = &ipv6_hdr(skb)->daddr;
++
++ memcpy(&fl6->daddr, addr6, sizeof(fl6->daddr));
++ neigh_release(neigh);
++ }
+ } else if (!fl6->flowi6_mark)
+ dst = dst_cache_get(&t->dst_cache);
+