Commit befb9254 authored by WANG Cong's avatar WANG Cong Committed by Greg Kroah-Hartman

ipv6: check skb->protocol before lookup for nexthop

[ Upstream commit 199ab00f ]

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: ea3dc960 ("ip6_tunnel: Add support for wildcard tunnel endpoints.")
Reported-by: default avatarAndrey Konovalov <andreyknvl@google.com>
Tested-by: default avatarAndrey Konovalov <andreyknvl@google.com>
Cc: Steffen Klassert <steffen.klassert@secunet.com>
Signed-off-by: default avatarCong Wang <xiyou.wangcong@gmail.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 114f0c66
...@@ -1049,7 +1049,7 @@ static int ip6_tnl_xmit2(struct sk_buff *skb, ...@@ -1049,7 +1049,7 @@ static int ip6_tnl_xmit2(struct sk_buff *skb,
struct ip6_tnl *t = netdev_priv(dev); struct ip6_tnl *t = netdev_priv(dev);
struct net *net = t->net; struct net *net = t->net;
struct net_device_stats *stats = &t->dev->stats; struct net_device_stats *stats = &t->dev->stats;
struct ipv6hdr *ipv6h = ipv6_hdr(skb); struct ipv6hdr *ipv6h;
struct ipv6_tel_txoption opt; struct ipv6_tel_txoption opt;
struct dst_entry *dst = NULL, *ndst = NULL; struct dst_entry *dst = NULL, *ndst = NULL;
struct net_device *tdev; struct net_device *tdev;
...@@ -1061,26 +1061,28 @@ static int ip6_tnl_xmit2(struct sk_buff *skb, ...@@ -1061,26 +1061,28 @@ static int ip6_tnl_xmit2(struct sk_buff *skb,
/* NBMA tunnel */ /* NBMA tunnel */
if (ipv6_addr_any(&t->parms.raddr)) { if (ipv6_addr_any(&t->parms.raddr)) {
struct in6_addr *addr6; if (skb->protocol == htons(ETH_P_IPV6)) {
struct neighbour *neigh; struct in6_addr *addr6;
int addr_type; struct neighbour *neigh;
int addr_type;
if (!skb_dst(skb)) if (!skb_dst(skb))
goto tx_err_link_failure; goto tx_err_link_failure;
neigh = dst_neigh_lookup(skb_dst(skb), neigh = dst_neigh_lookup(skb_dst(skb),
&ipv6_hdr(skb)->daddr); &ipv6_hdr(skb)->daddr);
if (!neigh) if (!neigh)
goto tx_err_link_failure; goto tx_err_link_failure;
addr6 = (struct in6_addr *)&neigh->primary_key; addr6 = (struct in6_addr *)&neigh->primary_key;
addr_type = ipv6_addr_type(addr6); addr_type = ipv6_addr_type(addr6);
if (addr_type == IPV6_ADDR_ANY) if (addr_type == IPV6_ADDR_ANY)
addr6 = &ipv6_hdr(skb)->daddr; addr6 = &ipv6_hdr(skb)->daddr;
memcpy(&fl6->daddr, addr6, sizeof(fl6->daddr)); memcpy(&fl6->daddr, addr6, sizeof(fl6->daddr));
neigh_release(neigh); neigh_release(neigh);
}
} else if (!(t->parms.flags & } else if (!(t->parms.flags &
(IP6_TNL_F_USE_ORIG_TCLASS | IP6_TNL_F_USE_ORIG_FWMARK))) { (IP6_TNL_F_USE_ORIG_TCLASS | IP6_TNL_F_USE_ORIG_FWMARK))) {
/* enable the cache only only if the routing decision does /* enable the cache only only if the routing decision does
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment