ip6_tunnel: make sure to pull inner header in __ip6_tnl_rcv()
[platform/kernel/linux-starfive.git] / net / ipv6 / ip6_tunnel.c
index 46c19bd..9bbabf7 100644 (file)
@@ -796,8 +796,8 @@ static int __ip6_tnl_rcv(struct ip6_tnl *tunnel, struct sk_buff *skb,
                                                struct sk_buff *skb),
                         bool log_ecn_err)
 {
-       const struct ipv6hdr *ipv6h = ipv6_hdr(skb);
-       int err;
+       const struct ipv6hdr *ipv6h;
+       int nh, err;
 
        if ((!(tpi->flags & TUNNEL_CSUM) &&
             (tunnel->parms.i_flags & TUNNEL_CSUM)) ||
@@ -829,7 +829,6 @@ static int __ip6_tnl_rcv(struct ip6_tnl *tunnel, struct sk_buff *skb,
                        goto drop;
                }
 
-               ipv6h = ipv6_hdr(skb);
                skb->protocol = eth_type_trans(skb, tunnel->dev);
                skb_postpull_rcsum(skb, eth_hdr(skb), ETH_HLEN);
        } else {
@@ -837,7 +836,23 @@ static int __ip6_tnl_rcv(struct ip6_tnl *tunnel, struct sk_buff *skb,
                skb_reset_mac_header(skb);
        }
 
+       /* Save offset of outer header relative to skb->head,
+        * because we are going to reset the network header to the inner header
+        * and might change skb->head.
+        */
+       nh = skb_network_header(skb) - skb->head;
+
        skb_reset_network_header(skb);
+
+       if (!pskb_inet_may_pull(skb)) {
+               DEV_STATS_INC(tunnel->dev, rx_length_errors);
+               DEV_STATS_INC(tunnel->dev, rx_errors);
+               goto drop;
+       }
+
+       /* Get the outer header. */
+       ipv6h = (struct ipv6hdr *)(skb->head + nh);
+
        memset(skb->cb, 0, sizeof(struct inet6_skb_parm));
 
        __skb_tunnel_rx(skb, tunnel->dev, tunnel->net);