[qca-nss-ecm] ECM Notifier changes

Added new atomic notifier support to notify external module for DB events.

Change-Id: I272a6d709d7957b949de64e71e2682a37afa94d2
Signed-off-by: Hardik S. Panchal <hpanchal@codeaurora.org>
diff --git a/Makefile b/Makefile
index 99bc5a5..8e78c5a 100644
--- a/Makefile
+++ b/Makefile
@@ -49,7 +49,8 @@
 	 ecm_classifier_default.o \
 	 ecm_interface.o \
 	 ecm_conntrack_notifier.o \
-	 ecm_init.o
+	 ecm_init.o \
+	 ecm_notifier.o
 
 # #############################################################################
 # Define ECM_FRONT_END_NSS_ENABLE=y in order to select
@@ -351,6 +352,7 @@
 ccflags-y += -DECM_INTERFACE_DEBUG_LEVEL=1
 ccflags-y += -DECM_STATE_DEBUG_LEVEL=1
 ccflags-y += -DECM_OPENWRT_SUPPORT=1
+ccflags-y += -DECM_NOTIFIER_DEBUG_LEVEL=1
 
 ccflags-y += -I$(obj)/ -I$(obj)/ecm_db -I$(obj)/frontends/include -I$(obj)/frontends/nss -I$(obj)/frontends/sfe -I$(obj)/exports
 ccflags-y += -Werror
diff --git a/ecm_db/ecm_db.c b/ecm_db/ecm_db.c
index 5434f1d..09ab70a 100644
--- a/ecm_db/ecm_db.c
+++ b/ecm_db/ecm_db.c
@@ -1,6 +1,6 @@
 /*
  **************************************************************************
- * Copyright (c) 2014-2018, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2014-2019, 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.
@@ -69,6 +69,7 @@
 #ifdef ECM_IPV6_ENABLE
 #include "ecm_front_end_ipv6.h"
 #endif
+#include "ecm_notifier_pvt.h"
 
 /*
  * Locking of the database - concurrency control
@@ -102,6 +103,11 @@
 };
 
 /*
+ * Global listener instance for DB events.
+ */
+static struct ecm_db_listener_instance *ecm_db_li;
+
+/*
  * ecm_db_adv_stats_state_write()
  *	Write out advanced stats state
  */
@@ -280,6 +286,25 @@
 		goto init_cleanup_4;
 	}
 
+	ecm_db_li = ecm_db_listener_alloc();
+	if (!ecm_db_li) {
+		DEBUG_ERROR("%p: Failed to allocate a listener instance\n", dentry);
+		goto init_cleanup_4;
+	}
+	ecm_db_listener_add(ecm_db_li,
+			NULL, /* ecm_notifier_iface_added */
+			NULL, /* ecm_notifier_iface_removed */
+			NULL, /* ecm_notifier_node_added */
+			NULL, /* ecm_notifier_node_removed */
+			NULL, /* ecm_notifier_host_added */
+			NULL, /* ecm_notifier_host_removed */
+			NULL, /* ecm_notifier_mapping_added */
+			NULL, /* ecm_notifier_mapping_removed */
+			ecm_notifier_connection_added,
+			ecm_notifier_connection_removed,
+			NULL, /* ecm_notifier_connection_final */
+			NULL);
+
 	/*
 	 * Initialize the timer resources.
 	 */
@@ -331,6 +356,11 @@
 	 */
 	ecm_db_timer_exit();
 
+	if (ecm_db_li) {
+		ecm_db_listener_deref(ecm_db_li);
+		ecm_db_li = NULL;
+	}
+
 	/*
 	 * Free the database.
 	 */
diff --git a/ecm_notifier.c b/ecm_notifier.c
new file mode 100644
index 0000000..2751c9d
--- /dev/null
+++ b/ecm_notifier.c
@@ -0,0 +1,197 @@
+/*
+ **************************************************************************
+ * Copyright (c) 2019, 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.
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ **************************************************************************
+ */
+
+#include <linux/version.h>
+#include <net/ip.h>
+#include <linux/inet.h>
+#include <linux/netfilter_bridge.h>
+#include <linux/atomic.h>
+
+/*
+ * Debug output levels
+ * 0 = OFF
+ * 1 = ASSERTS / ERRORS
+ * 2 = 1 + WARN
+ * 3 = 2 + INFO
+ * 4 = 3 + TRACE
+ */
+#define DEBUG_LEVEL ECM_NOTIFIER_DEBUG_LEVEL
+
+#include "ecm_types.h"
+#include "ecm_db_types.h"
+#include "ecm_state.h"
+#include "ecm_tracker.h"
+#include "ecm_classifier.h"
+#include "ecm_db.h"
+
+#include "ecm_notifier_pvt.h"
+#include "exports/ecm_notifier.h"
+
+static atomic_t ecm_notifier_count;
+static ATOMIC_NOTIFIER_HEAD(ecm_notifier_connection);
+
+/*
+ * ecm_notifier_ci_to_data()
+ * 	Convert ci to ecm_notifier_connection_data.
+ *
+ * This function holds reference to devices (data->from_dev & data->to_dev).
+ */
+static bool ecm_notifier_ci_to_data(struct ecm_db_connection_instance *ci, struct ecm_notifier_connection_data *data)
+{
+	ip_addr_t src_ip;
+	ip_addr_t dst_ip;
+	int32_t first_index;
+	struct ecm_db_iface_instance *interfaces[ECM_DB_IFACE_HEIRARCHY_MAX];
+
+	first_index = ecm_db_connection_interfaces_get_and_ref(ci, interfaces, ECM_DB_OBJ_DIR_FROM);
+	if (first_index == ECM_DB_IFACE_HEIRARCHY_MAX) {\
+		DEBUG_WARN("%p: Failed to get 'from' ifaces index\n", ci);
+		return false;
+	}
+	data->from_dev = dev_get_by_index(&init_net, ecm_db_iface_interface_identifier_get(interfaces[first_index]));
+	if (!data->from_dev) {
+		DEBUG_WARN("%p: Could not locate 'from' interface\n", ci);
+		return false;
+	}
+	ecm_db_connection_interfaces_deref(interfaces, first_index);
+
+	first_index = ecm_db_connection_interfaces_get_and_ref(ci, interfaces, ECM_DB_OBJ_DIR_TO);
+	if (first_index == ECM_DB_IFACE_HEIRARCHY_MAX) {
+		DEBUG_WARN("%p: Failed to get 'to' ifaces index\n", ci);
+		dev_put(data->from_dev);
+		return false;
+	}
+	data->to_dev = dev_get_by_index(&init_net, ecm_db_iface_interface_identifier_get(interfaces[first_index]));
+	if (!data->to_dev) {
+		DEBUG_WARN("%p: Could not locate 'to' interface\n", ci);
+		dev_put(data->from_dev);
+		return false;
+	}
+	ecm_db_connection_interfaces_deref(interfaces, first_index);
+
+	data->tuple.ip_ver = ecm_db_connection_ip_version_get(ci);
+	data->tuple.protocol = ecm_db_connection_protocol_get(ci);
+	data->tuple.src_port = ecm_db_connection_port_get(ci, ECM_DB_OBJ_DIR_FROM);
+	data->tuple.dst_port = ecm_db_connection_port_get(ci, ECM_DB_OBJ_DIR_TO);
+
+	ecm_db_connection_address_get(ci, ECM_DB_OBJ_DIR_FROM, src_ip);
+	ecm_db_connection_address_get(ci, ECM_DB_OBJ_DIR_TO, dst_ip);
+
+	switch (data->tuple.ip_ver) {
+	case 4:
+		ECM_IP_ADDR_TO_HIN4_ADDR(data->tuple.src.in.s_addr, src_ip);
+		ECM_IP_ADDR_TO_HIN4_ADDR(data->tuple.dest.in.s_addr, dst_ip);
+		break;
+
+	case 6:
+		ECM_IP_ADDR_TO_HIN6_ADDR(data->tuple.src.in6, src_ip);
+		ECM_IP_ADDR_TO_HIN6_ADDR(data->tuple.dest.in6, dst_ip);
+		break;
+
+	default:
+		/*
+		 * Shouldn't come here.
+		 */
+		DEBUG_ERROR("%p: Invalid protocol\n", ci);
+		dev_put(data->from_dev);
+		dev_put(data->to_dev);
+		return false;
+	}
+
+	return true;
+}
+
+/*
+ * ecm_notifier_connection_added()
+ * 	Send ECM connection added event to notifier chain.
+ */
+void ecm_notifier_connection_added(void *arg, struct ecm_db_connection_instance *ci)
+{
+	struct ecm_notifier_connection_data data = {0};
+
+	if (!atomic_read(&ecm_notifier_count)) {
+		DEBUG_TRACE("%p: No notifier has registered for event\n", ci);
+		return;
+	}
+
+	/*
+	 * Module has registered for events.
+	 */
+	if (!ecm_notifier_ci_to_data(ci, &data)) {
+		DEBUG_WARN("%p: Failed to get data from connection instance\n", ci);
+		return;
+	}
+
+	atomic_notifier_call_chain(&ecm_notifier_connection, ECM_NOTIFIER_ACTION_CONNECTION_ADDED, (void*)&data);
+
+	dev_put(data.from_dev);
+	dev_put(data.to_dev);
+}
+
+/*
+ * ecm_notifier_connection_removed()
+ * 	Send ECM connection removed event to notifier chain.
+ */
+void ecm_notifier_connection_removed(void *arg, struct ecm_db_connection_instance *ci)
+{
+	struct ecm_notifier_connection_data data = {0};
+
+	if (!atomic_read(&ecm_notifier_count)) {
+		DEBUG_TRACE("%p: No notifier has registered for event\n", ci);
+		return;
+	}
+
+	/*
+	 * Module has registered for events.
+	 */
+	if (!ecm_notifier_ci_to_data(ci, &data)) {
+		DEBUG_WARN("%p: Failed to get data from connection instance\n", ci);
+		return;
+	}
+
+	atomic_notifier_call_chain(&ecm_notifier_connection, ECM_NOTIFIER_ACTION_CONNECTION_REMOVED, (void*)&data);
+
+	dev_put(data.from_dev);
+	dev_put(data.to_dev);
+}
+
+/*
+ * ecm_notifier_register_connection_notify()
+ * 	Register for ECM connection events.
+ */
+int ecm_notifier_register_connection_notify(struct notifier_block *nb)
+{
+	/*
+	 * Currently atomic_notifier_chain_register does not return error and assumed to be always success.
+	 * so, incrmenting ecm_notifier_count at beginning.
+	 */
+	atomic_inc(&ecm_notifier_count);
+
+	return atomic_notifier_chain_register(&ecm_notifier_connection, nb);
+}
+EXPORT_SYMBOL(ecm_notifier_register_connection_notify);
+
+/*
+ * ecm_notifier_unregister_connection_notify()
+ * 	Unregister for ECM connection events.
+ */
+int ecm_notifier_unregister_connection_notify(struct notifier_block *nb)
+{
+	atomic_dec(&ecm_notifier_count);
+
+	return atomic_notifier_chain_unregister(&ecm_notifier_connection, nb);
+}
+EXPORT_SYMBOL(ecm_notifier_unregister_connection_notify);
diff --git a/ecm_notifier_pvt.h b/ecm_notifier_pvt.h
new file mode 100644
index 0000000..00a50d3
--- /dev/null
+++ b/ecm_notifier_pvt.h
@@ -0,0 +1,18 @@
+/*
+ **************************************************************************
+ * Copyright (c) 2019, 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.
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ **************************************************************************
+ */
+
+void ecm_notifier_connection_removed(void *arg, struct ecm_db_connection_instance *ci);
+void ecm_notifier_connection_added(void *arg, struct ecm_db_connection_instance *ci);
diff --git a/ecm_types.h b/ecm_types.h
index 56ffd92..2441503 100644
--- a/ecm_types.h
+++ b/ecm_types.h
@@ -1,6 +1,6 @@
 /*
  **************************************************************************
- * Copyright (c) 2014-2015 The Linux Foundation.  All rights reserved.
+ * Copyright (c) 2014-2015, 2019 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.
@@ -205,10 +205,10 @@
 	{ \
 		ecm_type_check_linux_ipv6(hin6); \
 		ecm_type_check_ecm_ip_addr(ipaddrt); \
-		in6.in6_u.u6_addr32[3] = ipaddrt[3]; \
-		in6.in6_u.u6_addr32[2] = ipaddrt[2]; \
-		in6.in6_u.u6_addr32[1] = ipaddrt[1]; \
-		in6.in6_u.u6_addr32[0] = ipaddrt[0]; \
+		hin6.in6_u.u6_addr32[3] = ipaddrt[3]; \
+		hin6.in6_u.u6_addr32[2] = ipaddrt[2]; \
+		hin6.in6_u.u6_addr32[1] = ipaddrt[1]; \
+		hin6.in6_u.u6_addr32[0] = ipaddrt[0]; \
 	}
 #endif
 
diff --git a/exports/ecm_notifier.h b/exports/ecm_notifier.h
new file mode 100644
index 0000000..04926da
--- /dev/null
+++ b/exports/ecm_notifier.h
@@ -0,0 +1,55 @@
+/*
+ **************************************************************************
+ * Copyright (c) 2019, 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.
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ **************************************************************************
+ */
+
+#ifndef __ECM_NOTIFIER_H__
+#define __ECM_NOTIFIER_H__
+
+/*
+ * ecm_notifier_connection_data
+ * 	Event data for a connection notification.
+ */
+struct ecm_notifier_connection_data {
+	struct net_device *to_dev;		/* First device in ECM 'to' iface hierarchy */
+	struct net_device *from_dev;		/* First device in ECM 'from' iface hierarchy */
+	struct {
+		union {
+			struct in_addr in;	/* IPv4 address in host order */
+			struct in6_addr in6;	/* IPv6 address in host order */
+		} src;
+		union {
+			struct in_addr in;	/* IPv4 address in host order */
+			struct in6_addr in6;	/* IPv6 address in host order */
+		} dest;
+		uint16_t dst_port;		/* Destination port in host order*/
+		uint16_t src_port;		/* Source port port in host order*/
+		uint8_t protocol;		/* Next protocol header number */
+		uint8_t ip_ver;			/* IP version 4 or 6 */
+	} tuple;
+};
+
+/*
+ * ecm_notifier_action
+ * 	Notifier action for all ECM events.
+ */
+enum ecm_notifier_action {
+	ECM_NOTIFIER_ACTION_CONNECTION_ADDED,
+	ECM_NOTIFIER_ACTION_CONNECTION_REMOVED,
+	ECM_NOTIFIER_ACTION_MAX
+};
+
+extern int ecm_notifier_register_connection_notify(struct notifier_block *nb);
+extern int ecm_notifier_unregister_connection_notify(struct notifier_block *nb);
+#endif