[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