Merge "[qca-nss-clients] Metadata based tunnel TX"
diff --git a/bridge/Makefile b/bridge/Makefile
index f9e445e..12f09c8 100644
--- a/bridge/Makefile
+++ b/bridge/Makefile
@@ -15,7 +15,7 @@
 ccflags-y += -DNSS_BRIDGE_MGR_PPE_SUPPORT
 endif
 
-ifeq ($(CONFIG_BONDING), $(filter $(CONFIG_BONDING), y m))
+ifneq (,$(filter $(CONFIG_BONDING),y m))
 ifneq ($(findstring 4.4, $(KERNELVERSION)),)
 ccflags-y += -DBONDING_SUPPORT
 endif
diff --git a/gre/nss_connmgr_gre_v4.c b/gre/nss_connmgr_gre_v4.c
index 29873da..a8897a3 100644
--- a/gre/nss_connmgr_gre_v4.c
+++ b/gre/nss_connmgr_gre_v4.c
@@ -1,6 +1,6 @@
 /*
  **************************************************************************
- * Copyright (c) 2017-2018 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2017-2018, 2020 The Linux Foundation. All rights reserved.
  * Permission to use, copy, modify, and/or distribute this software for
  * any purpose with or without fee is hereby granted, provided that the
  * above copyright notice and this permission notice appear in all copies.
@@ -93,8 +93,6 @@
 		return GRE_ERR_RADDR_ROUTE_LOOKUP;
 	}
 
-	rcu_read_lock();
-
 	neigh = dst_neigh_lookup(&rt->dst, (const void *)&raddr);
 	if (!neigh) {
 		neigh = neigh_lookup(&arp_tbl, (const void *)&raddr,  rt->dst.dev);
@@ -112,7 +110,6 @@
 		neigh = neigh_create(&arp_tbl, &raddr, rt->dst.dev);
 		if (IS_ERR_OR_NULL(neigh)) {
 			nss_connmgr_gre_warning("Unable to create ARP request neigh for %pI4\n", &raddr);
-			rcu_read_unlock();
 			ip_rt_put(rt);
 			return GRE_ERR_NEIGH_CREATE;
 		}
@@ -123,7 +120,6 @@
 	}
 
 	if (neigh->dev->type == ARPHRD_LOOPBACK) {
-		rcu_read_unlock();
 		ip_rt_put(rt);
 		neigh_release(neigh);
 		nss_connmgr_gre_warning("Err in destination MAC address, neighbour dev is loop back for %pI4\n", &raddr);
@@ -132,7 +128,6 @@
 	}
 
 	if (neigh->dev->flags & IFF_NOARP) {
-		rcu_read_unlock();
 		ip_rt_put(rt);
 		neigh_release(neigh);
 		nss_connmgr_gre_warning("Err in destination MAC address, neighbour dev is of type NO_ARP for %pI4\n", &raddr);
@@ -140,7 +135,6 @@
 	}
 
 	ether_addr_copy(dest_mac, neigh->ha);
-	rcu_read_unlock();
 	ip_rt_put(rt);
 	neigh_release(neigh);
 	nss_connmgr_gre_info("Destination MAC address for %pI4 is %pM\n", &raddr, dest_mac);
diff --git a/nss_connmgr_tunipip6.c b/nss_connmgr_tunipip6.c
index 6aab27d..d401bdf 100644
--- a/nss_connmgr_tunipip6.c
+++ b/nss_connmgr_tunipip6.c
@@ -29,7 +29,10 @@
 #include <linux/tcp.h>
 #include <linux/module.h>
 #include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include <net/ip.h>
 #include <net/ipv6.h>
+#include <linux/if.h>
 #if (LINUX_VERSION_CODE <= KERNEL_VERSION(3,9,0))
 #include <net/ipip.h>
 #else
@@ -94,7 +97,7 @@
  * nss_tunipip6_encap_exception()
  *	Exception handler registered to NSS driver.
  *
- * This function is called when no rule is found for successful encapsulation.
+ * Exception handler registered to NSS for handling tunipip6 ipv4 pkts.
  */
 static void nss_tunipip6_encap_exception(struct net_device *dev, struct sk_buff *skb, __attribute__((unused)) struct napi_struct *napi)
 {
@@ -114,20 +117,94 @@
  * nss_tunipip6_decap_exception()
  *	Exception handler registered to NSS driver.
  *
- * This function is called when no rule is found for successful decapsulation.
+ * Exception handler registered to NSS for handling tunipip6 ipv6 pkts.
  */
 static void nss_tunipip6_decap_exception(struct net_device *dev, struct sk_buff *skb, __attribute__((unused)) struct napi_struct *napi)
 {
-	skb->dev = dev;
-	nss_tunipip6_info("received - %d bytes name %s ver %x\n",
-			skb->len, dev->name, (skb->data[0] >> 4));
+	const struct net_device_ops *ops = dev->netdev_ops;
+	struct netdev_queue *queue;
+	struct iphdr *iph;
+	struct rtable *rt;
+	int cpu;
+	int8_t ver = skb->data[0] >> 4;
 
-	skb->protocol = htons(ETH_P_IPV6);
+	nss_tunipip6_trace("%p: received - %d bytes name %s ver %x\n",
+			dev, skb->len, dev->name, ver);
+
+	nss_tunipip6_assert(ver == 6);
+
+	if (unlikely(!pskb_may_pull(skb, sizeof(struct ipv6hdr)))) {
+		nss_tunipip6_warning("%p: pskb_may_pull failed to pull ipv6 header", dev);
+		dev_kfree_skb_any(skb);
+		return;
+	}
+
+	skb_pull(skb, sizeof(struct ipv6hdr));
+
+	if (unlikely(!pskb_may_pull(skb, sizeof(struct iphdr)))) {
+		nss_tunipip6_warning("%p: pskb_may_pull failed to linearize iphdr, packet does not have a proper IPv4 header.", dev);
+		dev_kfree_skb_any(skb);
+		return;
+	}
+
 	skb_reset_network_header(skb);
+
+	iph = ip_hdr(skb);
+	nss_tunipip6_assert(iph->version == 4);
+
+	rt = ip_route_output(&init_net, iph->daddr, 0, 0, 0);
+	if (unlikely(IS_ERR(rt))) {
+		nss_tunipip6_info("%p: Failed to find IPv4 route for %pI4\n", skb, &iph->daddr);
+		dev_kfree_skb_any(skb);
+		return;
+	}
+
+	nss_tunipip6_trace("%p: Route look up successful for dest_ip: %pI4 src_ip: %pI4\n",
+			skb, &iph->daddr, &iph->saddr);
+
+	skb_dst_drop(skb);
+	skb_dst_set(skb, &rt->dst);
+
+	skb_reset_transport_header(skb);
+
+	/*
+	 * Set ignore df bit to fragment the packet in kernel.
+	 */
+	if (!(iph->frag_off & htons(IP_DF))) {
+		skb->ignore_df = true;
+	}
+
+	skb->protocol = htons(ETH_P_IP);
 	skb->pkt_type = PACKET_HOST;
 	skb->skb_iif = dev->ifindex;
+	skb->dev = dev;
 	skb->ip_summed = CHECKSUM_NONE;
-	netif_receive_skb(skb);
+
+	/*
+	 * This is needed to acquire HARD_TX_LOCK.
+	 */
+	cpu = smp_processor_id();
+	queue = skb_get_tx_queue(dev, skb);
+
+	nss_tunipip6_trace("%p: skb queue mapping: %d, cpu: %d", skb, skb_get_queue_mapping(skb), cpu);
+
+	/*
+	 * Take HARD_TX_LOCK to be in sync with the kernel.
+	 */
+	HARD_TX_LOCK(dev, queue, cpu);
+
+	/*
+	 * Check if queue is alive
+	 */
+	if (unlikely(netif_xmit_frozen_or_stopped(queue))) {
+		HARD_TX_UNLOCK(dev, queue);
+		nss_tunipip6_trace("%p: Dropping the packet, as queue: %p is not alive", skb, queue);
+		skb_dst_drop(skb);
+		dev_kfree_skb_any(skb);
+		return;
+	}
+	ops->ndo_start_xmit(skb, dev);
+	HARD_TX_UNLOCK(dev, queue);
 }
 
 /*
diff --git a/pppoe/Makefile b/pppoe/Makefile
index 05e8749..1a2d899 100644
--- a/pppoe/Makefile
+++ b/pppoe/Makefile
@@ -5,7 +5,7 @@
 obj-m += qca-nss-pppoe.o
 qca-nss-pppoe-objs := nss_connmgr_pppoe.o
 
-ifeq ($(CONFIG_BONDING), $(filter $(CONFIG_BONDING), y m))
+ifneq (,$(filter $(CONFIG_BONDING),y m))
 ifneq ($(findstring 4.4, $(KERNELVERSION)),)
 ccflags-y += -DBONDING_SUPPORT
 endif
diff --git a/vlan/Makefile b/vlan/Makefile
index e623c82..ff2ac99 100644
--- a/vlan/Makefile
+++ b/vlan/Makefile
@@ -11,7 +11,7 @@
 ccflags-y += -DNSS_VLAN_MGR_DEBUG_LEVEL=0
 ccflags-y += -Werror
 
-ifeq ($(CONFIG_BONDING), $(filter $(CONFIG_BONDING), y m))
+ifneq (,$(filter $(CONFIG_BONDING),y m))
 ifneq ($(findstring 4.4, $(KERNELVERSION)),)
 ccflags-y += -DBONDING_SUPPORT
 endif