Merge branch 'ipvlan-Fix-insufficient-skb-linear-check'

Gao Feng says:

====================
ipvlan: Fix insufficient skb linear check

The current ipvlan codes use pskb_may_pull to get the skb linear header in
func ipvlan_get_L3_hdr, but the size isn't enough for arp and ipv6 icmp.
So it may access the unexpected momory in ipvlan_addr_lookup.
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
diff --git a/drivers/net/ipvlan/ipvlan_core.c b/drivers/net/ipvlan/ipvlan_core.c
index f2a7e92..11c1e79 100644
--- a/drivers/net/ipvlan/ipvlan_core.c
+++ b/drivers/net/ipvlan/ipvlan_core.c
@@ -116,7 +116,7 @@
 	return false;
 }
 
-static void *ipvlan_get_L3_hdr(struct sk_buff *skb, int *type)
+static void *ipvlan_get_L3_hdr(struct ipvl_port *port, struct sk_buff *skb, int *type)
 {
 	void *lyr3h = NULL;
 
@@ -124,7 +124,7 @@
 	case htons(ETH_P_ARP): {
 		struct arphdr *arph;
 
-		if (unlikely(!pskb_may_pull(skb, sizeof(*arph))))
+		if (unlikely(!pskb_may_pull(skb, arp_hdr_len(port->dev))))
 			return NULL;
 
 		arph = arp_hdr(skb);
@@ -165,8 +165,26 @@
 		/* Only Neighbour Solicitation pkts need different treatment */
 		if (ipv6_addr_any(&ip6h->saddr) &&
 		    ip6h->nexthdr == NEXTHDR_ICMP) {
+			struct icmp6hdr	*icmph;
+
+			if (unlikely(!pskb_may_pull(skb, sizeof(*ip6h) + sizeof(*icmph))))
+				return NULL;
+
+			ip6h = ipv6_hdr(skb);
+			icmph = (struct icmp6hdr *)(ip6h + 1);
+
+			if (icmph->icmp6_type == NDISC_NEIGHBOUR_SOLICITATION) {
+				/* Need to access the ipv6 address in body */
+				if (unlikely(!pskb_may_pull(skb, sizeof(*ip6h) + sizeof(*icmph)
+						+ sizeof(struct in6_addr))))
+					return NULL;
+
+				ip6h = ipv6_hdr(skb);
+				icmph = (struct icmp6hdr *)(ip6h + 1);
+			}
+
 			*type = IPVL_ICMPV6;
-			lyr3h = ip6h + 1;
+			lyr3h = icmph;
 		}
 		break;
 	}
@@ -510,7 +528,7 @@
 	struct ipvl_addr *addr;
 	int addr_type;
 
-	lyr3h = ipvlan_get_L3_hdr(skb, &addr_type);
+	lyr3h = ipvlan_get_L3_hdr(ipvlan->port, skb, &addr_type);
 	if (!lyr3h)
 		goto out;
 
@@ -539,7 +557,7 @@
 
 	if (!ipvlan_is_vepa(ipvlan->port) &&
 	    ether_addr_equal(eth->h_dest, eth->h_source)) {
-		lyr3h = ipvlan_get_L3_hdr(skb, &addr_type);
+		lyr3h = ipvlan_get_L3_hdr(ipvlan->port, skb, &addr_type);
 		if (lyr3h) {
 			addr = ipvlan_addr_lookup(ipvlan->port, lyr3h, addr_type, true);
 			if (addr) {
@@ -606,7 +624,7 @@
 	int addr_type;
 
 	if (ether_addr_equal(eth->h_source, skb->dev->dev_addr)) {
-		lyr3h = ipvlan_get_L3_hdr(skb, &addr_type);
+		lyr3h = ipvlan_get_L3_hdr(port, skb, &addr_type);
 		if (!lyr3h)
 			return true;
 
@@ -627,7 +645,7 @@
 	struct sk_buff *skb = *pskb;
 	rx_handler_result_t ret = RX_HANDLER_PASS;
 
-	lyr3h = ipvlan_get_L3_hdr(skb, &addr_type);
+	lyr3h = ipvlan_get_L3_hdr(port, skb, &addr_type);
 	if (!lyr3h)
 		goto out;
 
@@ -666,7 +684,7 @@
 	} else {
 		struct ipvl_addr *addr;
 
-		lyr3h = ipvlan_get_L3_hdr(skb, &addr_type);
+		lyr3h = ipvlan_get_L3_hdr(port, skb, &addr_type);
 		if (!lyr3h)
 			return ret;
 
@@ -717,7 +735,7 @@
 	if (!port || port->mode != IPVLAN_MODE_L3S)
 		goto out;
 
-	lyr3h = ipvlan_get_L3_hdr(skb, &addr_type);
+	lyr3h = ipvlan_get_L3_hdr(port, skb, &addr_type);
 	if (!lyr3h)
 		goto out;