From b7985e2d564754acf00def2e98f8594f85219258 Mon Sep 17 00:00:00 2001
From: Amit Kumar <amit.kumar@iopsys.eu>
Date: Wed, 28 Dec 2022 10:28:00 +0530
Subject: [PATCH] topologyd: redesigned handling for host stats

Redesigned the per host wan stats collection logic.
taken timestamp and ID of conntracck entry as key.
This reduces the memory allocation as well as cpu
utilization as the id being used now to get hash index
for faster access to node.
---
 src/host.c               |   2 +-
 src/host.h               |   1 -
 src/host_nodes.c         | 187 ++++++++++++++++++++-------------------
 src/host_nodes.h         |   4 +-
 src/ieee1905/topologyd.h |   3 +
 5 files changed, 101 insertions(+), 96 deletions(-)

diff --git a/src/host.c b/src/host.c
index 59b71a5..fca12b1 100644
--- a/src/host.c
+++ b/src/host.c
@@ -1405,7 +1405,7 @@ void host_remove_aged_node(struct topologyd_private *priv)
 					hwaddr_ntoa(p->hwaddr, mac_str);
 					config_del_section("hosts", "host", mac_str, priv->host_global_cfg.persistent);
 					dbg("Ageing timeout delecting mac node = ["MACFMT"]\n", MAC2STR(p->hwaddr));
-					host_node_del(priv->host.node_htable, p->hwaddr);
+					host_node_del(priv->host.node_htable, p->hwaddr, priv);
 					priv->host.num_nodes--;
 				}
 			}
diff --git a/src/host.h b/src/host.h
index e85ff1b..88e21e0 100644
--- a/src/host.h
+++ b/src/host.h
@@ -85,7 +85,6 @@ struct host_node {
 		unsigned long long rx_packets;
 		unsigned long long tx_packets;
 	} stats;
-	struct hlist_head nf_tc;
 	//char addrsrc[8];
 	enum address_type addrsrc;
 	unsigned int leasetmrmn;
diff --git a/src/host_nodes.c b/src/host_nodes.c
index e2765f6..05274f7 100644
--- a/src/host_nodes.c
+++ b/src/host_nodes.c
@@ -25,11 +25,16 @@
 #include "host_nodes.h"
 #include <libnetfilter_conntrack/libnetfilter_conntrack.h>
 
-
 struct host_nf_tc {
-	struct nf_conntrack *tc;
 	struct hlist_node hlist;
+	unsigned int id;
+	unsigned long long start_timestamp;
+	unsigned long long rx_bytes;
+        unsigned long long tx_bytes;
+	unsigned long long rx_packets;
+	unsigned long long tx_packets;
 	int in_use;
+	struct host_node *host;
 };
 
 void host_node_destroy(struct host_node *n)
@@ -69,7 +74,7 @@ struct host_node *host_node_lookup(struct hlist_head *table, uint8_t *hwaddr)
 	return NULL;
 }
 
-int host_node_del(struct hlist_head *table, uint8_t *hwaddr)
+int host_node_del(struct hlist_head *table, uint8_t *hwaddr, struct topologyd_private *priv)
 {
 	int hidx = node_hash(hwaddr);
 	struct host_node *n;
@@ -81,7 +86,7 @@ int host_node_del(struct hlist_head *table, uint8_t *hwaddr)
 	hlist_del(&n->hlist, &table[hidx]);
 	dbg("Node " MACFMT " removed from topology\n", MAC2STR(n->hwaddr));
 
-	host_node_nf_list_clean(n);
+	host_node_ct_list_clean(n, priv);
 
 	host_node_destroy(n);
 	return 0;
@@ -130,120 +135,119 @@ void host_node_print_all(struct hlist_head *table)
 	}
 }
 
-void host_node_nf_node_destroy(struct host_nf_tc *nf_tc)
+void host_node_ct_node_destroy(struct host_nf_tc *nf_tc)
 {
-	nfct_destroy(nf_tc->tc);
 	free(nf_tc);
 }
 
-void host_node_nfct_copy(struct nf_conntrack *tc, struct nf_conntrack *ct)
+void host_node_nfct_copy(struct host_nf_tc *host_ct, struct nf_conntrack *ct)
 {
-	nfct_copy(tc, ct, NFCT_CP_OVERRIDE);
+	host_ct->id = nfct_get_attr_u32(ct, ATTR_ID);
+	host_ct->start_timestamp = nfct_get_attr_u64(ct, ATTR_TIMESTAMP_START);
 }
 
-struct host_nf_tc *host_node_nf_tc_create(struct nf_conntrack *ct)
+struct host_nf_tc *host_node_ct_node_create(struct nf_conntrack *ct)
 {
 	struct host_nf_tc *n = NULL;
-	struct nf_conntrack *tc = NULL;
 
 	n = calloc(1, sizeof(struct host_nf_tc));
 	if (!n) {
 		warn("OOM: Node alloc failed!\n");
 		return NULL;
 	}
-	tc = nfct_new();
-	if (!tc) {
-		free(n);
-		warn("OOM: Node alloc failed!\n");
-		return NULL;
-	}
-	n->tc = tc;
 
-	host_node_nfct_copy(n->tc, ct);
+	host_node_nfct_copy(n, ct);
 
 	return n;
 }
 
-struct host_nf_tc *host_node_nf_node_lookup(struct hlist_head *table, struct nf_conntrack *ct)
+struct host_nf_tc *host_node_ct_node_lookup(struct topologyd_private *priv, struct nf_conntrack *ct)
 {
 	struct host_nf_tc *n = NULL;
+	unsigned int id;
+	unsigned long long start_timestamp;
 
-	hlist_for_each_entry(n, table, hlist) {
-		if (nfct_cmp(n->tc, ct, NFCT_CMP_ORIG))
+	id = nfct_get_attr_u32(ct, ATTR_ID);
+	start_timestamp = nfct_get_attr_u64(ct, ATTR_TIMESTAMP_START);
+	hlist_for_each_entry(n, &priv->host.ct_node_htable[ct_node_hash(id)], hlist) {
+		if (id == n->id && start_timestamp == n->start_timestamp)
 			return n;
 	}
 
 	return NULL;
 }
 
-void host_node_nf_list_clean(struct host_node *host)
+void host_node_ct_list_clean(const struct host_node *host, struct topologyd_private *priv)
 {
 	struct host_nf_tc *expired_entry = NULL;
-	struct hlist_node *p, *n;
+	struct host_nf_tc *n = NULL;
+	int i;
 
-	hlist_for_each_safe(p, n, &host->nf_tc){
-		expired_entry = container_of(p, struct host_nf_tc, hlist);
-		hlist_del(&expired_entry->hlist, &host->nf_tc);
-		host_node_nf_node_destroy(expired_entry);
+	for (i = 0; i < CT_NODE_HTABLE_SIZE ; i++) {
+		if (hlist_empty(&priv->host.ct_node_htable[i]))
+			continue;
+
+		hlist_for_each_entry(n, &priv->host.ct_node_htable[i], hlist) {
+			if(expired_entry) {
+				hlist_del(&expired_entry->hlist, &priv->host.ct_node_htable[i]);
+				host_node_ct_node_destroy(expired_entry);
+				expired_entry = NULL;
+			}
+			if (n->host && n->host == host)
+				expired_entry = n;
+		}
+		if(expired_entry) {
+				hlist_del(&expired_entry->hlist, &priv->host.ct_node_htable[i]);
+				host_node_ct_node_destroy(expired_entry);
+				expired_entry = NULL;
+		}
 	}
 }
 
-struct host_nf_tc *host_node_nf_node_add(struct hlist_head *table, struct nf_conntrack *ct)
+struct host_nf_tc *host_node_ct_node_add(struct nf_conntrack *ct, struct topologyd_private *priv, struct host_node *host)
 {
 	struct host_nf_tc *n = NULL;
 
-	n = host_node_nf_tc_create(ct);
+	n = host_node_ct_node_create(ct);
 	if (n) {
-		hlist_add_head(&n->hlist, table);
+		hlist_add_head(&n->hlist, &priv->host.ct_node_htable[ct_node_hash(n->id)]);
 		n->in_use = 1;
+		n->host = host;
 	}
 
 	return n;
 }
 
-int host_node_update_stats(struct nf_conntrack *ct, struct host_node *host)
+int host_node_update_stats(struct nf_conntrack *ct, struct host_nf_tc *ct_node)
 {
 	struct nfct_attr_grp_ctrs grp_ctrs_tx;
 	struct nfct_attr_grp_ctrs grp_ctrs_rx;
-	struct nfct_attr_grp_ctrs grp_ctrs_tx_prev;
-	struct nfct_attr_grp_ctrs grp_ctrs_rx_prev;
-	struct host_nf_tc *n;
 
-	memset(&grp_ctrs_tx_prev, 0, sizeof(struct nfct_attr_grp_ctrs));
-	memset(&grp_ctrs_rx_prev, 0, sizeof(struct nfct_attr_grp_ctrs));
 	memset(&grp_ctrs_tx, 0, sizeof(struct nfct_attr_grp_ctrs));
 	memset(&grp_ctrs_rx, 0, sizeof(struct nfct_attr_grp_ctrs));
 
 	nfct_get_attr_grp(ct, ATTR_GRP_ORIG_COUNTERS, &grp_ctrs_tx);
 	nfct_get_attr_grp(ct, ATTR_GRP_REPL_COUNTERS, &grp_ctrs_rx);
 
-	n = host_node_nf_node_lookup(&host->nf_tc, ct);
-	if (n) {
-		nfct_get_attr_grp(n->tc, ATTR_GRP_ORIG_COUNTERS, &grp_ctrs_tx_prev);
-		nfct_get_attr_grp(n->tc, ATTR_GRP_REPL_COUNTERS, &grp_ctrs_rx_prev);
-
-		if (grp_ctrs_tx.packets >= grp_ctrs_tx_prev.packets || grp_ctrs_rx.packets >= grp_ctrs_rx_prev.packets) {
-			host->stats.tx_packets += (grp_ctrs_tx.packets - grp_ctrs_tx_prev.packets);
-			host->stats.tx_bytes += (grp_ctrs_tx.bytes - grp_ctrs_tx_prev.bytes);
-			host->stats.rx_packets += (grp_ctrs_rx.packets - grp_ctrs_rx_prev.packets);
-			host->stats.rx_bytes += (grp_ctrs_rx.bytes - grp_ctrs_rx_prev.bytes);
+	if (ct_node) {
+
+		if (grp_ctrs_tx.packets >= ct_node->tx_packets || grp_ctrs_rx.packets >= ct_node->rx_packets) {
+			ct_node->host->stats.tx_packets += (grp_ctrs_tx.packets - ct_node->tx_packets);
+			ct_node->host->stats.tx_bytes += (grp_ctrs_tx.bytes - ct_node->tx_bytes);
+			ct_node->host->stats.rx_packets += (grp_ctrs_rx.packets - ct_node->rx_packets);
+			ct_node->host->stats.rx_bytes += (grp_ctrs_rx.bytes - ct_node->rx_bytes);
 		}
 		else {
-			host->stats.tx_packets += grp_ctrs_tx.packets;
-			host->stats.tx_bytes += grp_ctrs_tx.bytes;
-			host->stats.rx_packets += grp_ctrs_rx.packets;
-			host->stats.rx_bytes += grp_ctrs_rx.bytes;
-		}
-		host_node_nfct_copy(n->tc, ct);
-		n->in_use = 1;
-	}
-	else {
-		if (host_node_nf_node_add(&host->nf_tc, ct)) {
-			host->stats.tx_packets += grp_ctrs_tx.packets;
-			host->stats.tx_bytes += grp_ctrs_tx.bytes;
-			host->stats.rx_packets += grp_ctrs_rx.packets;
-			host->stats.rx_bytes += grp_ctrs_rx.bytes;
+			ct_node->host->stats.tx_packets += grp_ctrs_tx.packets;
+			ct_node->host->stats.tx_bytes += grp_ctrs_tx.bytes;
+			ct_node->host->stats.rx_packets += grp_ctrs_rx.packets;
+			ct_node->host->stats.rx_bytes += grp_ctrs_rx.bytes;
 		}
+		ct_node->in_use = 1;
+		ct_node->tx_packets = grp_ctrs_tx.packets;
+		ct_node->rx_packets = grp_ctrs_rx.packets;
+		ct_node->tx_bytes = grp_ctrs_tx.bytes;
+		ct_node->rx_bytes = grp_ctrs_rx.bytes;
 	}
 
         return NFCT_CB_CONTINUE;
@@ -262,6 +266,7 @@ int host_node_ipv6_stats_received(enum nf_conntrack_msg_type type,
 	char ipv6_addr[sizeof(int)*4];
 	char ipv6_dst_addr[sizeof(int)*4];
 	int  addr_part;
+	struct host_nf_tc *ct_node = NULL;
 
 	nfct_get_attr_grp(ct, ATTR_GRP_ORIG_IPV6, &grp_ipv6);
 	addr_part = (grp_ipv6.src[0]);
@@ -288,6 +293,10 @@ int host_node_ipv6_stats_received(enum nf_conntrack_msg_type type,
 	if (host_is_device_address(priv, ipv6_dst_addr, AF_INET6))
 		return NFCT_CB_CONTINUE;
 
+	ct_node = host_node_ct_node_lookup(priv, ct);
+	if (ct_node)
+		return host_node_update_stats(ct, ct_node);
+
 	for (i = 0; i < NODE_HTABLE_SIZE ; i++) {
 		if ((hlist_empty(&priv->host.node_htable[i])))
 			continue;
@@ -303,7 +312,8 @@ int host_node_ipv6_stats_received(enum nf_conntrack_msg_type type,
 				inet_pton(AF_INET6, host->ipv6addr[count], &sa6.sin6_addr);
 
 				if (!memcmp(ipv6_addr, sa6.sin6_addr.s6_addr, sizeof(int)*4)) {
-					return host_node_update_stats(ct, host);
+					ct_node = host_node_ct_node_add(ct, priv, host);
+					return host_node_update_stats(ct, ct_node);
 				}
 			}
 		}
@@ -323,6 +333,7 @@ int host_node_stats_received(enum nf_conntrack_msg_type type,
 	struct sockaddr_in sa;
 	int count;
 	struct in_addr dest_addr;
+	struct host_nf_tc *ct_node = NULL;
 
 	ret_val=nfct_get_attr_grp(ct, ATTR_GRP_ORIG_IPV4, &grp_ipv4);
 	if (ret_val || priv == NULL)
@@ -332,6 +343,10 @@ int host_node_stats_received(enum nf_conntrack_msg_type type,
 	if (host_is_device_address(priv, inet_ntoa(dest_addr), AF_INET))
 		return NFCT_CB_CONTINUE;
 
+	ct_node = host_node_ct_node_lookup(priv, ct);
+	if (ct_node)
+		return host_node_update_stats(ct, ct_node);
+
 	for (i = 0; i < NODE_HTABLE_SIZE ; i++) {
 		if ((hlist_empty(&priv->host.node_htable[i])))
 			continue;
@@ -348,7 +363,8 @@ int host_node_stats_received(enum nf_conntrack_msg_type type,
 				inet_pton(AF_INET, host->ipv4addr_list[count], &(sa.sin_addr));
 
 				if (grp_ipv4.src == sa.sin_addr.s_addr) {
-					return host_node_update_stats(ct, host);
+					ct_node = host_node_ct_node_add(ct, priv, host);
+					return host_node_update_stats(ct, ct_node);
 				}
 			}
 		}
@@ -377,6 +393,7 @@ void host_node_nfct_query(struct nfct_handle *cth, int family, struct topologyd_
 
         filter_dump = nfct_filter_dump_create();
         if (filter_dump == NULL) {
+		nfct_destroy(ct);
 		err("%s Failed to allocate filter_dump", __FUNCTION__);
 		return;
 	}
@@ -396,30 +413,23 @@ void host_node_clean_nf_tc(struct topologyd_private *priv)
 	int i;
 	struct host_nf_tc *expired_entry = NULL;
 	struct host_nf_tc *n = NULL;
-	struct host_node *host = NULL;
 
-	for (i = 0; i < NODE_HTABLE_SIZE ; i++) {
-		if ((hlist_empty(&priv->host.node_htable[i])))
+	for (i = 0; i < CT_NODE_HTABLE_SIZE ; i++) {
+		if ((hlist_empty(&priv->host.ct_node_htable[i])))
 			continue;
-
-                hlist_for_each_entry(host, &priv->host.node_htable[i], hlist) {
-                        if (hwaddr_is_zero(host->hwaddr) || (host->is_copy == 1) ||
-				(host->intf_type != HOST_TYPE_ETHER && host->intf_type !=HOST_TYPE_WIFI))
-                                continue;
-			hlist_for_each_entry(n, &host->nf_tc, hlist) {
-				if(expired_entry) {
-					hlist_del(&expired_entry->hlist, &host->nf_tc);
-					host_node_nf_node_destroy(expired_entry);
-					expired_entry = NULL;
-				}
-				if (!n->in_use)
-					expired_entry = n;
+		hlist_for_each_entry(n, &priv->host.ct_node_htable[i], hlist) {
+			if(expired_entry) {
+				hlist_del(&expired_entry->hlist, &priv->host.ct_node_htable[i]);
+				host_node_ct_node_destroy(expired_entry);
+				expired_entry = NULL;
+			}
+			if (!n->in_use)
+				expired_entry = n;
 		}
 		if(expired_entry) {
-				hlist_del(&expired_entry->hlist, &host->nf_tc);
-				host_node_nf_node_destroy(expired_entry);
+				hlist_del(&expired_entry->hlist, &priv->host.ct_node_htable[i]);
+				host_node_ct_node_destroy(expired_entry);
 				expired_entry = NULL;
-			}
 		}
 	}
 }
@@ -428,21 +438,14 @@ int host_node_get_statistics(struct topologyd_private *priv)
 {
         struct nfct_handle *cth;
 	struct host_nf_tc *n = NULL;
-	struct host_node *host = NULL;
 	int i = 0;
 
-	for (i = 0; i < NODE_HTABLE_SIZE ; i++) {
-		if ((hlist_empty(&priv->host.node_htable[i])))
+	for (i = 0; i < CT_NODE_HTABLE_SIZE ; i++) {
+		if ((hlist_empty(&priv->host.ct_node_htable[i])))
 			continue;
 
-                hlist_for_each_entry(host, &priv->host.node_htable[i], hlist) {
-                        if (hwaddr_is_zero(host->hwaddr) || (host->is_copy == 1) ||
-				(host->intf_type != HOST_TYPE_ETHER && host->intf_type !=HOST_TYPE_WIFI))
-                                continue;
-
-			hlist_for_each_entry(n, &host->nf_tc, hlist) {
-				n->in_use = 0;
-			}
+                hlist_for_each_entry(n, &priv->host.ct_node_htable[i], hlist) {
+			n->in_use = 0;
 		}
 	}
 
diff --git a/src/host_nodes.h b/src/host_nodes.h
index 9df6d06..f1671a7 100644
--- a/src/host_nodes.h
+++ b/src/host_nodes.h
@@ -6,9 +6,9 @@
 struct host_node *host_node_create(uint8_t *hwaddr);
 void host_node_free(struct host_node *n);
 struct host_node *host_node_add(struct hlist_head *table, uint8_t *hwaddr);
-int host_node_del(struct hlist_head *table, uint8_t *hwaddr);
+int host_node_del(struct hlist_head *table, uint8_t *hwaddr, struct topologyd_private *priv);
 struct host_node *host_node_lookup(struct hlist_head *table, uint8_t *hwaddr);
 void host_node_print_all(struct hlist_head *table);
-void host_node_nf_list_clean(struct host_node *host);
+void host_node_ct_list_clean(const struct host_node *host, struct topologyd_private *priv);
 
 #endif /* HOST_NODES_H */
diff --git a/src/ieee1905/topologyd.h b/src/ieee1905/topologyd.h
index c838402..9c40d65 100644
--- a/src/ieee1905/topologyd.h
+++ b/src/ieee1905/topologyd.h
@@ -219,6 +219,8 @@ struct topology_changelog {
 #define NODE_HTABLE_SIZE	128
 #define node_hash(n)	\
 	((n[0] ^ n[1] ^ n[2] ^ n[3] ^ n[4] ^ n[5]) % NODE_HTABLE_SIZE)
+#define CT_NODE_HTABLE_SIZE	128
+#define ct_node_hash(n)		(n % 128)
 
 
 struct topology {
@@ -243,6 +245,7 @@ struct host_ntwk {
 	void *priv;
 	int32_t num_nodes;
 	struct hlist_head node_htable[NODE_HTABLE_SIZE];
+	struct hlist_head ct_node_htable[CT_NODE_HTABLE_SIZE];
 };
 
 struct topologyd_private {
-- 
GitLab