Merge "[qca-nss-clients] Fix SA encap/decap stats"
diff --git a/ipsecmgr/v2.0/nss_ipsecmgr_sa.c b/ipsecmgr/v2.0/nss_ipsecmgr_sa.c
index 33569d1..5e38341 100644
--- a/ipsecmgr/v2.0/nss_ipsecmgr_sa.c
+++ b/ipsecmgr/v2.0/nss_ipsecmgr_sa.c
@@ -768,7 +768,7 @@
 	/*
 	 * Drop counters starts after common stats counters
 	 */
-	drop_counters = (uint32_t *)((uint8_t *)&sa_stats + sizeof(sa_stats->cmn_stats));
+	drop_counters = (uint32_t *)((uint8_t *)sa_stats + sizeof(sa_stats->cmn_stats));
 	num_counters = (sizeof(*sa_stats) - sizeof(sa_stats->cmn_stats)) / sizeof(uint32_t);
 
 	for (i = 0; i < num_counters; i++)
diff --git a/ipsecmgr/v2.0/plugins/klips/nss_ipsec_klips.c b/ipsecmgr/v2.0/plugins/klips/nss_ipsec_klips.c
index b564f12..5cd49e6 100644
--- a/ipsecmgr/v2.0/plugins/klips/nss_ipsec_klips.c
+++ b/ipsecmgr/v2.0/plugins/klips/nss_ipsec_klips.c
@@ -67,6 +67,7 @@
  */
 struct nss_ipsec_klips_skb_cb {
 	struct net_device *hlos_dev;
+	struct sock *sk;
 	uint32_t flags;
 	uint16_t replay_win;
 	uint16_t magic;
@@ -93,9 +94,11 @@
  * the table
  */
 struct nss_ipsec_klips_tun {
+	struct list_head sa_list;
+	sk_encap_rcv_method_t sk_encap_rcv;
 	struct net_device *klips_dev;
 	struct net_device *nss_dev;
-	struct list_head sa_list;
+	struct sock *sk;
 };
 
 /*
@@ -710,6 +713,46 @@
 }
 
 /*
+ * nss_ipsec_klips_fallback_natt_handler()
+ *	Invoke KLIPS encap recieve handler for socket.
+ */
+static int nss_ipsec_klips_fallback_natt_handler(struct sock *sk, struct sk_buff *skb)
+{
+	struct nss_ipsec_klips_tun *tun;
+	sk_encap_rcv_method_t encap_rcv;
+
+	read_lock(&tunnel_map.lock);
+
+	tun = nss_ipsec_klips_get_tun_by_addr(skb);
+	if (!tun) {
+		read_unlock(&tunnel_map.lock);
+		nss_ipsec_klips_warn("%p: Unable to find tunnel assciated, dropping skb", skb);
+		goto drop_skb;
+	}
+
+	if (tun->sk != sk) {
+		read_unlock(&tunnel_map.lock);
+		nss_ipsec_klips_warn("%p: Packet recieved from incorrect socket, dropping skb", tun);
+		goto drop_skb;
+	}
+
+	encap_rcv = tun->sk_encap_rcv;
+	if (!encap_rcv) {
+		read_unlock(&tunnel_map.lock);
+		nss_ipsec_klips_warn("%p: NULL sk_encap_rcv, dropping skb", tun);
+		goto drop_skb;
+	}
+
+	read_unlock(&tunnel_map.lock);
+
+	return encap_rcv(sk, skb);
+
+drop_skb:
+	dev_kfree_skb_any(skb);
+	return 0;
+}
+
+/*
  * nss_ipsec_klips_init_trans_offload()
  * 	Reset all headers added by KLIPS for transport mode.
  */
@@ -960,7 +1003,7 @@
 	tun = nss_ipsec_klips_get_tun_by_addr(skb);
 	if (!tun) {
 		write_unlock(&tunnel_map.lock);
-		return nss_ipsec_klips_fallback_esp_handler(skb);
+		return -ENODEV;
 	}
 
 	ifindex = tun->klips_dev->ifindex;
@@ -975,11 +1018,11 @@
 	 */
 	if (!nss_ipsecmgr_sa_verify(nss_dev, sa_tuple)) {
 		dev_put(nss_dev);
-		return nss_ipsec_klips_fallback_esp_handler(skb);
+		return -ENODATA;
 	}
 
 	/*
-	 * In case of Decap, skb->data points to ESP header.
+	 * In case of Decap, skb->data points to ESP header or UDP header for NATT.
 	 * Set the skb->data to outer IP header.
 	 */
 	skb_push(skb, skb->data - skb_network_header(skb));
@@ -1032,13 +1075,127 @@
 	struct nss_ipsecmgr_flow_tuple flow_tuple = {0};
 	struct nss_ipsecmgr_sa_tuple sa_tuple = {0};
 	uint8_t ttl;
+	int ret;
 
 	nss_ipsec_klips_trace("%p: SKb recieved by KLIPS plugin\n", skb);
 
 	nss_ipsec_klips_outer2sa_tuple(skb_network_header(skb), false, &sa_tuple, &ttl, true);
 	nss_ipsec_klips_outer2flow_tuple(skb_network_header(skb), false, &flow_tuple);
 
-	return nss_ipsec_klips_offload_outer(skb, &sa_tuple, &flow_tuple);
+	ret = nss_ipsec_klips_offload_outer(skb, &sa_tuple, &flow_tuple);
+	if (ret) {
+		nss_ipsec_klips_trace("%p: Fallback to klips esp handler. error(%d)\n", skb, ret);
+		return nss_ipsec_klips_fallback_esp_handler(skb);
+	}
+
+	return 0;
+}
+
+/*
+ * nss_ipsec_klips_offload_natt()
+ * 	Socket encap recieve handler for IPsec UDP encapsulated packets.
+ *
+ * Shell returns the following value:
+ * =0 if SKB is consumed.
+ * >0 if skb should be passed on to UDP.
+ * <0 if skb should be resubmitted.
+ */
+int nss_ipsec_klips_offload_natt(struct sock *sk, struct sk_buff *skb)
+{
+	size_t hdr_len = sizeof(struct udphdr) +  sizeof(struct ip_esp_hdr);
+	struct nss_ipsecmgr_flow_tuple flow_tuple = {0};
+	struct nss_ipsecmgr_sa_tuple sa_tuple = {0};
+	struct ip_esp_hdr *esph;
+	uint8_t ttl;
+	int status;
+
+	/*
+	 * Socket has to be of type UDP_ENCAP_ESPINUDP .
+	 */
+	BUG_ON(udp_sk(sk)->encap_type != UDP_ENCAP_ESPINUDP);
+
+	/*
+	 * NAT-keepalive packet has udphdr & one byte payload (rfc3948).
+	 */
+	if (skb->len < hdr_len) {
+		goto fallback;
+	}
+
+	/*
+	 * In case of non-linear SKB we would like to ensure that
+	 * all the required headers are present in the first segment
+	 */
+	if (skb_is_nonlinear(skb) && (skb_headlen(skb) < hdr_len)) {
+		if (skb_linearize(skb)) {
+			dev_kfree_skb_any(skb);
+			return 0;
+		}
+
+		/*
+		 * skb_linearize may change header. So, reload all required pointer.
+		 */
+		skb_reset_transport_header(skb);
+		skb_set_network_header(skb, -(int)sizeof(struct iphdr));
+	}
+
+	/*
+	 * Check if packet has non-ESP marker (rfc3948)
+	 */
+	esph = (struct ip_esp_hdr *)(skb_transport_header(skb) + sizeof(struct udphdr));
+	if (ntohl(esph->spi) == NSS_IPSEC_KLIPS_NON_ESP_MARKER) {
+		goto fallback;
+	}
+
+	/*
+	 * ESP packet recieved, offload it to NSS else send it to KLIPS.
+	 */
+	nss_ipsec_klips_outer2sa_tuple(skb_network_header(skb), true, &sa_tuple, &ttl, true);
+	nss_ipsec_klips_outer2flow_tuple(skb_network_header(skb), true, &flow_tuple);
+
+	status = nss_ipsec_klips_offload_outer(skb, &sa_tuple, &flow_tuple);
+	if (status) {
+		nss_ipsec_klips_trace("%p: Fallback to klips natt handler. error(%d)\n", skb, status);
+		goto fallback;
+	}
+
+	return 0;
+
+fallback:
+	return nss_ipsec_klips_fallback_natt_handler(sk, skb);
+}
+
+/*
+ * nss_ipsec_klips_register_natt_handler()
+ * 	Hold and set the encap recieve handler of socket with offload method.
+ */
+static void nss_ipsec_klips_register_natt_handler(struct nss_ipsec_klips_tun *tun, struct sock *sk)
+{
+	/*
+	 * write lock is needed as we are modifying tunnel entry.
+	 */
+	BUG_ON(write_can_lock(&tunnel_map.lock));
+
+	sock_hold(sk);
+	tun->sk_encap_rcv = udp_sk(sk)->encap_rcv;
+	tun->sk = sk;
+	xchg(&udp_sk(sk)->encap_rcv, nss_ipsec_klips_offload_natt);
+}
+
+/*
+ * nss_ipsec_klips_unregister_natt_handler()
+ * 	 Release socket and revert encap recieve handler to original.
+ */
+static void nss_ipsec_klips_unregister_natt_handler(struct nss_ipsec_klips_tun *tun, struct sock *sk)
+{
+	/*
+	 * write lock is needed as we are modifying tunnel entry.
+	 */
+	BUG_ON(write_can_lock(&tunnel_map.lock));
+
+	xchg(&udp_sk(tun->sk)->encap_rcv, tun->sk_encap_rcv);
+	sock_put(tun->sk);
+	tun->sk = NULL;
+	tun->sk_encap_rcv = NULL;
 }
 
 /*
@@ -1296,8 +1453,15 @@
 		return -EINVAL;
 	}
 
+	/*
+	 * Convert socket to use our (de)encapsulation routine and save original pointers in tun map.
+	 */
+	if (natt && !tun->sk && skb_cb->sk) {
+		nss_ipsec_klips_info("%p: Updating sock(%p) encap_rcv handler\n", tun, skb_cb->sk);
+		nss_ipsec_klips_register_natt_handler(tun, skb_cb->sk);
+	}
+
 	write_unlock(&tunnel_map.lock);
-	nss_ipsec_klips_trace("%p: Decap SA rule message sent\n", tun);
 
 	return 0;
 }
@@ -1509,6 +1673,8 @@
 		tunnel_map.used++;
 		tunnel_map.tbl[index].nss_dev = nss_dev;
 		tunnel_map.tbl[index].klips_dev = klips_dev;
+		tunnel_map.tbl[index].sk = NULL;
+		tunnel_map.tbl[index].sk_encap_rcv = NULL;
 		dev_hold(klips_dev);
 
 		write_unlock_bh(&tunnel_map.lock);
@@ -1560,6 +1726,15 @@
 		 */
 		tun->nss_dev = NULL;
 		tun->klips_dev = NULL;
+
+		/*
+		 * Revert socket encap_rcv. Those fields are only used for NATT.
+		 */
+		if (tun->sk) {
+			nss_ipsec_klips_info("%p: Releasing socket(%p)\n", tun, tun->sk);
+			nss_ipsec_klips_unregister_natt_handler(tun, tun->sk);
+		}
+
 		tunnel_map.used--;
 
 		/*
diff --git a/ipsecmgr/v2.0/plugins/klips/nss_ipsec_klips.h b/ipsecmgr/v2.0/plugins/klips/nss_ipsec_klips.h
index 68f33e8..1382d21 100644
--- a/ipsecmgr/v2.0/plugins/klips/nss_ipsec_klips.h
+++ b/ipsecmgr/v2.0/plugins/klips/nss_ipsec_klips.h
@@ -22,6 +22,11 @@
 /**
  * @file klips plugin for ipsec manager.
  */
+
+#define NSS_IPSEC_KLIPS_NON_ESP_MARKER 0x0000
+
+typedef int (*sk_encap_rcv_method_t)(struct sock *sk, struct sk_buff *skb);
+
 #define NSS_IPSEC_KLIPS_DEBUG_LVL_ERROR 1
 #define NSS_IPSEC_KLIPS_DEBUG_LVL_WARN 2
 #define NSS_IPSEC_KLIPS_DEBUG_LVL_INFO 3