[qca-nss-sfe] add support for GRE tunnel handling.

Change-Id: Ie4bcdb2df8b13a7c3d63b79eac7ef8f1668ee9c5
Signed-off-by: Nitin Shetty <quic_nitinsj@quicinc.com>
diff --git a/sfe_ipv6.c b/sfe_ipv6.c
index 1bbc608..5d1cbf8 100644
--- a/sfe_ipv6.c
+++ b/sfe_ipv6.c
@@ -32,6 +32,9 @@
 #include <linux/inetdevice.h>
 #include <linux/netfilter_ipv6.h>
 #include <net/protocol.h>
+#include <net/addrconf.h>
+#include <net/gre.h>
+
 #include "sfe_debug.h"
 #include "sfe_api.h"
 #include "sfe.h"
@@ -42,6 +45,7 @@
 #include "sfe_ipv6_icmp.h"
 #include "sfe_pppoe.h"
 #include "sfe_ipv6_tunipip6.h"
+#include "sfe_ipv6_gre.h"
 
 #define sfe_ipv6_addr_copy(src, dest) memcpy((void *)(dest), (void *)(src), 16)
 
@@ -90,6 +94,11 @@
 	"TUNIPIP6_SMALL_TTL",
 	"TUNIPIP6_NEEDS_FRAGMENTATION",
 	"TUNIPIP6_SYNC_ON_FIND"
+	"GRE_HEADER_INCOMPLETE",
+	"GRE_NO_CONNECTION",
+	"GRE_IP_OPTIONS_OR_INITIAL_FRAGMENT",
+	"GRE_SMALL_TTL",
+	"GRE_NEEDS_FRAGMENTATION"
 };
 
 static struct sfe_ipv6 __si6;
@@ -742,6 +751,25 @@
 }
 
 /*
+ * sfe_ipv6_is_local_ip()
+ *	return true if it is local ip otherwise return false
+ */
+static bool sfe_ipv6_is_local_ip(struct sfe_ipv6 *si, uint8_t *addr)
+{
+	struct net_device *dev;
+	struct in6_addr ip_addr;
+	memcpy(ip_addr.s6_addr, addr, 16);
+
+	dev = ipv6_dev_find(&init_net, &ip_addr, 1);
+	if (dev) {
+		dev_put(dev);
+		return true;
+	}
+
+	return false;
+}
+
+/*
  * sfe_ipv6_recv()
  *	Handle packet receives and forwaring.
  *
@@ -831,6 +859,12 @@
 		return sfe_ipv6_recv_tunipip6(si, skb, dev, len, iph, ihl, sync_on_find, l2_info, true);
 	}
 
+#ifdef SFE_GRE_TUN_ENABLE
+	if (IPPROTO_GRE == next_hdr) {
+		return sfe_ipv6_recv_gre(si, skb, dev, len, iph, ihl, sync_on_find, tun_outer);
+	}
+#endif
+
 	sfe_ipv6_exception_stats_inc(si, SFE_IPV6_EXCEPTION_EVENT_UNHANDLED_PROTOCOL);
 	DEBUG_TRACE("not UDP, TCP or ICMP: %u\n", next_hdr);
 	return 0;
@@ -1198,6 +1232,10 @@
 		}
 	}
 
+	if ((IPPROTO_GRE == tuple->protocol) && !sfe_ipv6_is_local_ip(si, (uint8_t *)original_cm->match_dest_ip)) {
+		original_cm->flags |= SFE_IPV6_CONNECTION_MATCH_FLAG_PASSTHROUGH;
+	}
+
 #ifdef CONFIG_NF_FLOW_COOKIE
 	original_cm->flow_cookie = 0;
 #endif
@@ -1214,7 +1252,7 @@
 	 * are used. In such cases, do not use HW csum offload. csum offload is used only when we
 	 * are sending directly to the destination interface that supports it.
 	 */
-	if (likely(dest_dev->features & NETIF_F_HW_CSUM) && !netif_is_vxlan(dest_dev)) {
+	if (likely(dest_dev->features & NETIF_F_HW_CSUM) && sfe_dev_has_hw_csum(dest_dev)) {
 		if ((msg->conn_rule.return_top_interface_num == msg->conn_rule.return_interface_num) ||
 			(msg->rule_flags & SFE_RULE_CREATE_FLAG_USE_RETURN_BOTTOM_INTERFACE)) {
 			 original_cm->flags |= SFE_IPV6_CONNECTION_MATCH_FLAG_CSUM_OFFLOAD;
@@ -1347,6 +1385,10 @@
 		reply_cm->flags |= SFE_IPV6_CONNECTION_MATCH_FLAG_BRIDGE_FLOW;
 	}
 
+	if ((IPPROTO_GRE == tuple->protocol) && !sfe_ipv6_is_local_ip(si, (uint8_t *)reply_cm->match_dest_ip)) {
+		reply_cm->flags |= SFE_IPV6_CONNECTION_MATCH_FLAG_PASSTHROUGH;
+	}
+
 	/*
 	 * Setup UDP Socket if found to be valid for decap.
 	 */
@@ -1441,6 +1483,32 @@
 		reply_cm->flow_accel = 1;
 	}
 #endif
+
+	/*
+	 * the inet6_protocol handler will be used only in decap path
+	 * for non passthrough case.
+	 */
+	original_cm->proto = NULL;
+	reply_cm->proto = NULL;
+
+#ifdef SFE_GRE_TUN_ENABLE
+	if (!(reply_cm->flags & SFE_IPV6_CONNECTION_MATCH_FLAG_PASSTHROUGH)) {
+		rcu_read_lock();
+		reply_cm->proto = rcu_dereference(inet6_protos[tuple->protocol]);
+		rcu_read_unlock();
+
+		if (unlikely(!reply_cm->proto)) {
+			kfree(reply_cm);
+			kfree(original_cm);
+			kfree(c);
+			dev_put(src_dev);
+			dev_put(dest_dev);
+			DEBUG_WARN("sfe: GRE proto handler is not registered\n");
+			return -EPERM;
+		}
+	}
+#endif
+
 	/*
 	 * Decapsulation path have proto set.
 	 * This is used to differentiate de/encap, and call protocol specific handler.
@@ -1457,7 +1525,7 @@
 	 * are used. In such cases, do not use HW csum offload. csum offload is used only when we
 	 * are sending directly to the destination interface that supports it.
 	 */
-	if (likely(src_dev->features & NETIF_F_HW_CSUM) && !(netif_is_vxlan(src_dev) || netif_is_vxlan(dest_dev))) {
+	if (likely(src_dev->features & NETIF_F_HW_CSUM) && sfe_dev_has_hw_csum(src_dev)) {
 		if ((msg->conn_rule.flow_top_interface_num == msg->conn_rule.flow_interface_num) ||
 			(msg->rule_flags & SFE_RULE_CREATE_FLAG_USE_FLOW_BOTTOM_INTERFACE)) {
 			 reply_cm->flags |= SFE_IPV6_CONNECTION_MATCH_FLAG_CSUM_OFFLOAD;