[qca-nss-clients] Support for GRE tunnel

Change-Id: I13959f881f0c07ee6f2bb99957e20461800a76c7
Signed-off-by: Suman Ghosh <sumaghos@codeaurora.org>
diff --git a/gre/nss_connmgr_gre.c b/gre/nss_connmgr_gre.c
index adaf7c3..ccaaaec 100644
--- a/gre/nss_connmgr_gre.c
+++ b/gre/nss_connmgr_gre.c
@@ -1,6 +1,6 @@
 /*
  **************************************************************************
- * Copyright (c) 2017 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2017-2018 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.
@@ -353,25 +353,21 @@
 }
 
 /*
- * nss_connmgr_gre_exception()
+ * nss_connmgr_gre_tap_exception()
+ * 	Exception handler for GRETAP device
  */
-static void nss_connmgr_gre_exception(struct net_device *dev, struct sk_buff *skb,
+static void nss_connmgr_gre_tap_exception(struct net_device *dev, struct sk_buff *skb,
 					  __attribute__((unused)) struct napi_struct *napi)
 {
 
 	struct ethhdr *eth_hdr;
-	if (unlikely(!enable_notifier)) {
-		nss_connmgr_gre_error("%p: NSS GRE exception handler called\n", dev);
-		dev_kfree_skb_any(skb);
-		return;
-	}
-
 	if (unlikely(!pskb_may_pull(skb, sizeof(struct ethhdr)))) {
 		nss_connmgr_gre_warning("%p: pskb_may_pull failed for skb:%p\n", dev, skb);
 		dev_kfree_skb_any(skb);
 		return;
 	}
 	eth_hdr = (struct ethhdr *)skb->data;
+	nss_connmgr_gre_warning("%p: eth_hdr->h_proto: %d\n", dev, eth_hdr->h_proto);
 	if (likely(ntohs(eth_hdr->h_proto) >= ETH_P_802_3_MIN)) {
 		switch (ntohs(eth_hdr->h_proto)) {
 		case ETH_P_IP:
@@ -380,14 +376,14 @@
 				dev_kfree_skb_any(skb);
 				return;
 			}
-			return nss_connmgr_gre_v4_exception(dev, skb);
+			return nss_connmgr_gre_tap_v4_exception(dev, skb);
 		case ETH_P_IPV6:
 			if (unlikely(!pskb_may_pull(skb, sizeof(struct ipv6hdr)))) {
 				nss_connmgr_gre_warning("%p: pskb_may_pull failed for skb:%p\n", dev, skb);
 				dev_kfree_skb_any(skb);
 				return;
 			}
-			return nss_connmgr_gre_v6_exception(dev, skb);
+			return nss_connmgr_gre_tap_v6_exception(dev, skb);
 		default:
 			break;
 		}
@@ -402,6 +398,38 @@
 }
 
 /*
+ * nss_connmgr_gre_tun_exception()
+ * 	Exception handler for GRETUN device
+ */
+static void nss_connmgr_gre_tun_exception(struct net_device *dev, struct sk_buff *skb,
+					  __attribute__((unused)) struct napi_struct *napi)
+{
+	struct iphdr *iph;
+
+	if (unlikely(!pskb_may_pull(skb, sizeof(struct iphdr)))) {
+		nss_connmgr_gre_warning("%p: pskb_may_pull failed for skb:%p\n", dev, skb);
+		dev_kfree_skb_any(skb);
+		return;
+	}
+	iph = (struct iphdr *)skb->data;
+	switch (iph->version) {
+		case 4:
+			return nss_connmgr_gre_tun_v4_exception(dev, skb);
+		case 6:
+			if (unlikely(!pskb_may_pull(skb, sizeof(struct ipv6hdr)))) {
+				nss_connmgr_gre_warning("%p: pskb_may_pull failed for skb:%p\n", dev, skb);
+				dev_kfree_skb_any(skb);
+				return;
+			}
+			return nss_connmgr_gre_tun_v6_exception(dev, skb);
+		default:
+			nss_connmgr_gre_warning("%p: wrong IP version set to skb:%p\n", dev, skb);
+			dev_kfree_skb_any(skb);
+			break;
+	}
+}
+
+/*
  * nss_connmgr_gre_rx_pkt()
  *	GRE Tap function to receive packet from NSS.
  */
@@ -817,7 +845,7 @@
 	struct nss_gre_config_msg *cmsg = &req.msg.cmsg;
 	int if_number;
 	uint32_t features = 0;
-	struct nss_ctx_instance *nss_ctx;
+	struct nss_ctx_instance *nss_ctx = NULL;
 	nss_tx_status_t status;
 	struct net_device *next_dev = NULL;
 
@@ -851,11 +879,25 @@
 	/*
 	 * Register gre tunnel with NSS
 	 */
-	nss_ctx = nss_gre_register_if(if_number,
-				       nss_connmgr_gre_exception,
-				       nss_connmgr_gre_event_receive,
-				       dev,
-				       features);
+	if ((dev->type == ARPHRD_IPGRE) || (dev->type == ARPHRD_IP6GRE)) {
+		/*
+		 * GRE Tunnel mode
+		 */
+		nss_ctx = nss_gre_register_if(if_number,
+				nss_connmgr_gre_tun_exception,
+				nss_connmgr_gre_event_receive,
+				dev,
+				features);
+	} else {
+		/*
+		 * GRE Tap mode
+		 */
+		nss_ctx = nss_gre_register_if(if_number,
+				nss_connmgr_gre_tap_exception,
+				nss_connmgr_gre_event_receive,
+				dev,
+				features);
+	}
 
 	if (!nss_ctx) {
 		status = nss_dynamic_interface_dealloc_node(if_number, NSS_DYNAMIC_INTERFACE_TYPE_GRE);
diff --git a/gre/nss_connmgr_gre.h b/gre/nss_connmgr_gre.h
index 011518d..1de1b38 100644
--- a/gre/nss_connmgr_gre.h
+++ b/gre/nss_connmgr_gre.h
@@ -1,6 +1,6 @@
 /*
  **************************************************************************
- * Copyright (c) 2017 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2017-2018 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.
@@ -85,7 +85,10 @@
 int nss_connmgr_gre_v4_get_config(struct net_device *dev, struct nss_gre_msg *req, struct net_device **next_dev, bool hold);
 int nss_connmgr_gre_v6_get_config(struct net_device *dev, struct nss_gre_msg *req, struct net_device **next_dev, bool hold);
 
-void nss_connmgr_gre_v4_exception(struct net_device *dev, struct sk_buff *skb);
-void nss_connmgr_gre_v6_exception(struct net_device *dev, struct sk_buff *skb);
+void nss_connmgr_gre_tap_v4_exception(struct net_device *dev, struct sk_buff *skb);
+void nss_connmgr_gre_tap_v6_exception(struct net_device *dev, struct sk_buff *skb);
+
+void nss_connmgr_gre_tun_v4_exception(struct net_device *dev, struct sk_buff *skb);
+void nss_connmgr_gre_tun_v6_exception(struct net_device *dev, struct sk_buff *skb);
 
 #endif
diff --git a/gre/nss_connmgr_gre_v4.c b/gre/nss_connmgr_gre_v4.c
index 111720f..f830773 100644
--- a/gre/nss_connmgr_gre_v4.c
+++ b/gre/nss_connmgr_gre_v4.c
@@ -1,6 +1,6 @@
 /*
  **************************************************************************
- * Copyright (c) 2017 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2017-2018 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.
@@ -220,9 +220,10 @@
 }
 
 /*
- * nss_connmgr_gre_v4_exception()
+ * nss_connmgr_gre_tap_v4_exception()
+ * 	Handle IPv4 exception for GRETAP
  */
-void nss_connmgr_gre_v4_exception(struct net_device *dev, struct sk_buff *skb)
+void nss_connmgr_gre_tap_v4_exception(struct net_device *dev, struct sk_buff *skb)
 {
 	struct ethhdr *eth_hdr = (struct ethhdr *)skb->data;
 	struct iphdr *iph = (struct iphdr *)(eth_hdr + 1);
@@ -247,6 +248,10 @@
 		dev_kfree_skb_any(skb);
 		return;
 	}
+
+	/*
+	 * TODO: Support parsing GRE options
+	 */
 	skb_pull(skb, (sizeof(struct ethhdr) + sizeof(struct iphdr)
 				+ sizeof(struct gre_base_hdr)));
 
@@ -269,6 +274,67 @@
 }
 
 /*
+ * nss_connmgr_gre_tun_v4_exception()
+ * 	Handle IPv4 exception for GRETUN
+ */
+void nss_connmgr_gre_tun_v4_exception(struct net_device *dev, struct sk_buff *skb)
+{
+	struct iphdr *iph_outer = (struct iphdr *)skb->data;
+	struct iphdr *iph_inner;
+
+	if (iph_outer->protocol != IPPROTO_GRE) {
+		/*
+		 * These are decapped IP packets.
+		 */
+		if (iph_outer->version == 4) {
+			skb->protocol = htons(ETH_P_IP);
+		} else if (iph_outer->version == 6){
+			skb->protocol = htons(ETH_P_IPV6);
+		} else {
+			nss_connmgr_gre_info("%p: wrong IP version in GRE decapped packet. skb: :%p\n", dev, skb);
+			dev_kfree_skb_any(skb);
+			return;
+		}
+		skb->pkt_type = PACKET_HOST;
+		skb->dev = dev;
+
+		netif_receive_skb(skb);
+		return;
+	}
+
+	/*
+	 * GRE encapsulated packet exceptioned, remove the encapsulation
+	 * and transmit on GRE interface.
+	 */
+	if (unlikely(!pskb_may_pull(skb, sizeof(struct iphdr) + sizeof(struct gre_base_hdr)))) {
+		nss_connmgr_gre_warning("%p: pskb_may_pull failed for skb:%p\n", dev, skb);
+		dev_kfree_skb_any(skb);
+		return;
+	}
+
+	/*
+	 * TODO: Support parsing GRE options
+	 */
+	skb_pull(skb, sizeof(struct iphdr) + sizeof(struct gre_base_hdr));
+	iph_inner = (struct iphdr *)skb->data;
+	skb->dev = dev;
+	if (iph_inner->version == 4) {
+		skb->protocol = htons(ETH_P_IP);
+	} else if (iph_inner->version == 6){
+		skb->protocol = htons(ETH_P_IPV6);
+	} else {
+		nss_connmgr_gre_info("%p: wrong IP version in GRE encapped packet. skb: %p\n", dev, skb);
+		dev_kfree_skb_any(skb);
+		return;
+	}
+	skb_reset_mac_header(skb);
+	skb_reset_network_header(skb);
+	skb_reset_transport_header(skb);
+	skb_reset_mac_len(skb);
+	dev_queue_xmit(skb);
+}
+
+/*
  * nss_connmgr_gre_v4_get_config()
  *	Fill in config message to send to NSS.
  */
diff --git a/gre/nss_connmgr_gre_v6.c b/gre/nss_connmgr_gre_v6.c
index f3c3ef8..bb41fb3 100644
--- a/gre/nss_connmgr_gre_v6.c
+++ b/gre/nss_connmgr_gre_v6.c
@@ -1,6 +1,6 @@
 /*
  **************************************************************************
- * Copyright (c) 2017 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2017-2018 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.
@@ -122,9 +122,10 @@
 }
 
 /*
- * nss_connmgr_gre_v6_exception()
+ * nss_connmgr_gre_tap_v6_exception()
+ * 	Handle IPv6 exception for GRETAP
  */
-void nss_connmgr_gre_v6_exception(struct net_device *dev, struct sk_buff *skb)
+void nss_connmgr_gre_tap_v6_exception(struct net_device *dev, struct sk_buff *skb)
 {
 	struct ethhdr *eth = (struct ethhdr *)skb->data;
 	struct ipv6hdr *ip6h = (struct ipv6hdr *)(eth + 1);
@@ -149,6 +150,10 @@
 		dev_kfree_skb_any(skb);
 		return;
 	}
+
+	/*
+	 * TODO: Support parsing GRE options
+	 */
 	skb_pull(skb, (sizeof(struct ethhdr) + sizeof(struct ipv6hdr)
 				+ sizeof(struct gre_base_hdr)));
 
@@ -171,6 +176,68 @@
 }
 
 /*
+ * nss_connmgr_gre_tun_v6_exception()
+ * 	Handle IPv6 exception for GRETUN
+ */
+void nss_connmgr_gre_tun_v6_exception(struct net_device *dev, struct sk_buff *skb)
+{
+	struct ipv6hdr *ip6h_outer = (struct ipv6hdr *)skb->data;
+	struct ipv6hdr *ip6h_inner;
+
+	if (ip6h_outer->nexthdr != IPPROTO_GRE) {
+		/*
+		 * These are decapped IP packets.
+		 */
+		if (ip6h_outer->version == 4) {
+			skb->protocol = htons(ETH_P_IP);
+		} else if (ip6h_outer->version == 6){
+			skb->protocol = htons(ETH_P_IPV6);
+		} else {
+			nss_connmgr_gre_info("%p: wrong IP version in GRE decapped packet. skb: %p\n", dev, skb);
+			dev_kfree_skb_any(skb);
+			return;
+		}
+		skb->pkt_type = PACKET_HOST;
+		skb->dev = dev;
+
+		netif_receive_skb(skb);
+		return;
+	}
+
+	/*
+	 * GRE encapsulated packet exceptioned, remove the encapsulation
+	 * and transmit on GRE interface.
+	 */
+	if (unlikely(!pskb_may_pull(skb, sizeof(struct ipv6hdr) + sizeof(struct gre_base_hdr)))) {
+		nss_connmgr_gre_warning("%p: pskb_may_pull failed for skb:%p\n", dev, skb);
+		dev_kfree_skb_any(skb);
+		return;
+	}
+
+	/*
+	 * TODO: Support parsing GRE options
+	 */
+	skb_pull(skb, sizeof(struct ipv6hdr) + sizeof(struct gre_base_hdr));
+
+	ip6h_inner = (struct ipv6hdr *)skb->data;
+	skb->dev = dev;
+	if (ip6h_inner->version == 4) {
+		skb->protocol = htons(ETH_P_IP);
+	} else if (ip6h_inner->version == 6){
+		skb->protocol = htons(ETH_P_IPV6);
+	} else {
+		nss_connmgr_gre_info("%p: wrong IP version in GRE encapped packet. skb: %p\n", dev, skb);
+		dev_kfree_skb_any(skb);
+		return;
+	}
+	skb_reset_mac_header(skb);
+	skb_reset_network_header(skb);
+	skb_reset_transport_header(skb);
+	skb_reset_mac_len(skb);
+	dev_queue_xmit(skb);
+}
+
+/*
  * nss_connmgr_gre_v6_set_config()
  *	Set user config to dev inerface.
  */