Add v4.4.100 version of CDU

This copy of client data usage was taken from the commit prior to
8fb99a366d4fb06be25f9c8f5daac78c6f40e759 and copied to this directory.

Change-Id: Ie68035d0bfd2b731f2e5a600fb978916199e8dd9
diff --git a/client_data_usage_brcm_mtk/cdu_db.c b/client_data_usage_brcm_mtk/cdu_db.c
new file mode 100644
index 0000000..3f06928
--- /dev/null
+++ b/client_data_usage_brcm_mtk/cdu_db.c
@@ -0,0 +1,464 @@
+/* cdu_db.c
+ *
+ * This file defines client data usage database.
+ *
+ * Author: Cradlepoint Technology, Inc.  <source@cradlepoint.com>
+ *		Adrian Sitterle <asitterle@cradlepoint.com>
+ *
+ * Copyright (C) 2019 Cradlepoint Technology, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/version.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/jhash.h>
+#include <linux/list_sort.h>
+#include <linux/etherdevice.h>
+#include <net/netfilter/nf_conntrack.h>
+#include <net/netfilter/nf_conntrack_core.h>
+#include <net/netfilter/nf_conntrack_acct.h>
+
+#include "cdu_db.h"
+#include "cdu_xt_target.h"
+
+struct hlist_head cdu_db_usage_hash[USAGE_HASH_BITS] __read_mostly;
+static int cdu_db_usage_count __read_mostly;
+struct list_head cdu_db_usage_list; /* Sorted list */
+DEFINE_SPINLOCK(cdu_db_hash_lock);
+
+static unsigned int jhash_rnd __read_mostly;
+
+/* Use tuple 0 (original dir) to deterine lan ip info */
+static const union nf_inet_addr *get_lan_ip_from_ct(const struct nf_conn *ct)
+{
+
+	if (ct->orig_direction == CDU_DIR_DEST)
+		return &ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.u3;
+	else if (ct->orig_direction == CDU_DIR_SRC)
+		return &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u3;
+
+	CDU_DEBUG("Direction is not set, but should be at this point. Don't know which ip to use.\n");
+	return NULL;
+}
+
+/* Deterine lan mac info provided by hook in nf_conntrack_in() */
+static const unsigned char *get_lan_mac_from_ct(const struct nf_conn *ct)
+{
+
+	if (ct->orig_direction != CDU_DIR_NOT_SET)
+		return ct->mac;
+
+	CDU_DEBUG("Direction is not set, but should be at this point. Don't know which mac to use.\n");
+	return NULL;
+}
+
+/* Use tuple 0 (original dir) to deterine lan l3 proto info */
+static unsigned char get_lan_family_from_ct(const struct nf_conn *ct)
+{
+	return ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.l3num;
+}
+
+/* Calculate htable bucket key */
+static inline unsigned int calc_hash_key_from_ct(const struct nf_conn *ct)
+{
+	unsigned char family;
+	const union nf_inet_addr *addr;
+
+	family = get_lan_family_from_ct(ct);
+	addr = get_lan_ip_from_ct(ct);
+
+	if (!addr)
+		return 0;
+
+	if (family == NFPROTO_IPV4)
+		return jhash((const char *)&addr->ip, IP_ALEN, jhash_rnd) & (USAGE_HASH_BITS - 1);
+
+	if (family == NFPROTO_IPV6)
+		return jhash((const char *)&addr->ip6, IP6_ALEN, jhash_rnd) & (USAGE_HASH_BITS - 1);
+
+	return 0;
+}
+
+/* Try to find an entry in the cdu_db_usage_hash table based on information in ct */
+static struct usage_entry *cdu_db_find_entry(const struct nf_conn *ct)
+{
+	unsigned int key;
+	struct usage_entry *obj = NULL;
+	unsigned char family;
+	const union nf_inet_addr *paddr;
+	const unsigned char *mac;
+
+	family = get_lan_family_from_ct(ct);
+	paddr = get_lan_ip_from_ct(ct);
+	mac = get_lan_mac_from_ct(ct);
+
+	if (!paddr || !mac)
+		return NULL;
+
+	key = calc_hash_key_from_ct(ct);
+	if (hlist_empty(&cdu_db_usage_hash[key]))
+		return NULL;
+
+	hlist_for_each_entry(obj, &cdu_db_usage_hash[key], node) {
+		CDU_DEBUG("searching... obj key=%u ip=%pI4 ip6=%pI6 mac=%pM\n", key, &obj->addr.ip, &obj->addr.ip6, obj->mac);
+		/* find first ip, which will always be last updated entry due to hlist_add_head */
+		if ((family == NFPROTO_IPV4 && (obj->addr.ip == paddr->ip)) || (family == NFPROTO_IPV6 && !memcmp(obj->addr.ip6, paddr->ip6, IP6_ALEN))) {
+			if (!mac || ether_addr_equal(mac, obj->mac)) {
+				CDU_DEBUG("Found entry in htable ^\n");
+				return obj;
+			}
+		}
+	}
+
+	CDU_DEBUG("Did not find entry in htable.\n");
+	return NULL;
+}
+
+static int cdu_last_activity_time_cmp(void * priv, struct list_head *a, struct list_head *b)
+{
+	struct usage_entry * obj_a = container_of(a, struct usage_entry, lnode);
+	struct usage_entry * obj_b = container_of(b, struct usage_entry, lnode);
+
+	if (obj_a->last_time < obj_b->last_time)
+		return -1;
+
+	if(obj_a->last_time > obj_b->last_time)
+		return 1;
+
+	return 0;
+
+}
+
+static struct list_head const* sort_cdu_db(void)
+{
+#ifdef DEBUG_ENABLE
+	struct timespec start;
+	struct timespec end;
+	get_monotonic_boottime(&start);
+#endif
+
+	if (list_empty(&cdu_db_usage_list))
+		return NULL;
+
+	list_sort(NULL, &cdu_db_usage_list, cdu_last_activity_time_cmp);
+#ifdef DEBUG_ENABLE
+	get_monotonic_boottime(&end);
+	CDU_DEBUG("%s took %lu ns\n", __func__, end.tv_nsec - start.tv_nsec);
+#endif
+	return &cdu_db_usage_list;
+}
+
+static void cdu_drop_clients(void)
+{
+	struct list_head const * sorted_list;
+	struct list_head  * pos, *n;
+	struct usage_entry *obj;
+	unsigned int client_drop_count = CDU_USAGE_DROP_CHUNK;
+	sorted_list = sort_cdu_db();
+
+	if (!sorted_list)
+		return;
+	list_for_each_safe(pos, n, sorted_list) {
+		if (--client_drop_count == 0)
+			return;
+		obj = container_of(pos, struct usage_entry, lnode);
+		CDU_INFO("DROPPPING MAC = %pm, IP = %pI4, IP6 = %pI6, Last_Time = %lu, First_Time = %lu, Connect_Time = %lu\n",
+					obj->mac, &obj->addr.ip, &obj->addr.ip6,
+					obj->last_time, obj->first_time, obj->last_time - obj->first_time);
+		cdu_db_remove(obj);
+	}
+}
+
+/* Add new entry to the hash table, based on information in ct */
+static struct usage_entry *cdu_db_add_entry(const struct nf_conn *ct)
+{
+	struct usage_entry *obj;
+	struct timespec now;
+	const union nf_inet_addr *paddr;
+	const unsigned char *mac;
+
+	paddr = get_lan_ip_from_ct(ct);
+	mac = get_lan_mac_from_ct(ct);
+
+	if (!paddr || !mac)
+		return NULL;
+
+	if (unlikely(cdu_db_usage_count >= CDU_USAGE_MAXENTRY)) {
+		CDU_ERROR("Usage limit exceeded, dropping oldest clients\n");
+		cdu_drop_clients();
+	}
+
+	obj = kzalloc(sizeof(struct usage_entry), GFP_ATOMIC);
+
+	get_monotonic_boottime(&now);
+	obj->last_time = obj->first_time = (unsigned long) now.tv_sec;
+
+	ether_addr_copy(obj->mac, mac);
+	obj->family = get_lan_family_from_ct(ct);
+
+	if (obj->family == NFPROTO_IPV4)
+		obj->addr.ip = paddr->ip;
+	else if (obj->family == NFPROTO_IPV6)
+		memcpy(&obj->addr.ip6, &paddr->ip6, IP6_ALEN);
+
+	obj->key = calc_hash_key_from_ct(ct);
+
+	hlist_add_head(&obj->node, &cdu_db_usage_hash[obj->key]);
+	list_add(&obj->lnode, &cdu_db_usage_list);
+	cdu_db_usage_count++;
+
+	if (obj->family == NFPROTO_IPV4)
+		CDU_INFO("found new client ip=%pI4 mac=%pM (%d clients)\n", &obj->addr.ip, obj->mac, cdu_db_usage_count);
+	else if (obj->family == NFPROTO_IPV6)
+		CDU_INFO("found new client ip6=%pI6 mac=%pM (%d clients)\n", &obj->addr.ip6, obj->mac, cdu_db_usage_count);
+
+	return obj;
+}
+
+/* external entry point for cdu_xt_target to add missing db entries
+ * handles locking to avoid external needing to lock/find/add/unlock
+ * necessary to utilize unconditional add_entry
+ */
+void cdu_db_add_entry_ifmissing(const struct nf_conn *ct)
+{
+	spin_lock_bh(&cdu_db_hash_lock);
+
+	if (!cdu_db_find_entry(ct))
+		cdu_db_add_entry(ct);
+
+	spin_unlock_bh(&cdu_db_hash_lock);
+}
+
+/* Update information of a client in htable.
+ * persist = CDU_DB_UPDATE_PERMA_SET (1), if called at the end of conntrack flow
+ * persist = CDU_DB_UPDATE_EPHEM_SET (0), if called in the middle of a conntrack flow
+ */
+void cdu_db_update_entry(const struct nf_conn *ct, bool persist, bool clear)
+{
+	struct nf_conn_acct *acct;
+	struct timespec now;
+	u_int64_t bytes_up, bytes_dn, packets_up, packets_dn;
+
+#ifdef ZERO_EPHEM_FLOWS
+	struct nf_conn_counter *zeroed;
+#endif
+	struct nf_conn_counter *counter;
+	struct usage_entry *obj = NULL;
+	int up_dir = 0, dl_dir = 0;
+	int dataset = STATSET(persist);
+
+	if (ct->orig_direction == CDU_DIR_DEST)
+		up_dir = 1;
+	else if (ct->orig_direction == CDU_DIR_SRC)
+		dl_dir = 1;
+	else {
+		CDU_DEBUG("Unknown direction\n");
+		return;
+	}
+
+	acct = nf_conn_acct_find(ct);
+	if (!acct) {
+		CDU_DEBUG("Could not find conntrack acct entry for this ct.\n");
+		return;
+	}
+	counter = acct->counter;
+
+	spin_lock_bh(&cdu_db_hash_lock);
+
+	obj = cdu_db_find_entry(ct);
+	if (!obj) {
+		obj = cdu_db_add_entry(ct);
+		if (!obj)
+			goto update_exit;
+	}
+
+	get_monotonic_boottime(&now);
+	obj->last_time = (unsigned long) now.tv_sec;
+
+	bytes_up = atomic64_read(&counter[up_dir].bytes);
+	packets_up = atomic64_read(&counter[up_dir].packets);
+	bytes_dn = atomic64_read(&counter[dl_dir].bytes);
+	packets_dn = atomic64_read(&counter[dl_dir].packets);
+
+	obj->upload[dataset].bytes += bytes_up;
+	obj->upload[dataset].packets += packets_up;
+	obj->download[dataset].bytes += bytes_dn;
+	obj->download[dataset].packets += packets_dn;
+
+#ifdef ZERO_EPHEM_FLOWS
+	zeroed  = acct->zeroed;
+
+	/* remember what we need to subtract later */
+	obj->upload[CLEARED_COUNTER].bytes += atomic64_read(&zeroed[up_dir].bytes);
+	obj->upload[CLEARED_COUNTER].packets += atomic64_read(&zeroed[up_dir].packets);
+	obj->download[CLEARED_COUNTER].bytes += atomic64_read(&zeroed[dl_dir].bytes);
+	obj->download[CLEARED_COUNTER].packets += atomic64_read(&zeroed[dl_dir].packets);
+	if (clear) {
+		atomic64_set(&zeroed[up_dir].bytes, bytes_up);
+		atomic64_set(&zeroed[up_dir].packets, packets_up);
+		atomic64_set(&zeroed[dl_dir].bytes, bytes_dn);
+		atomic64_set(&zeroed[dl_dir].packets, packets_dn);
+	}
+#endif
+
+	CDU_DEBUG("Flow stats (set:%s) up: packets=%llu bytes %llu, dl: packets=%llu bytes %llu\n",
+			(dataset)?"PERMA":"EPHEM",
+			(uint64_t)atomic64_read(&counter[up_dir].packets), (uint64_t)atomic64_read(&counter[up_dir].bytes),
+			(uint64_t)atomic64_read(&counter[dl_dir].packets), (uint64_t)atomic64_read(&counter[dl_dir].bytes));
+	CDU_DEBUG("Updated %s client stats upload packets=%llu bytes %llu\n",
+			(persist == CDU_DB_UPDATE_PERMA_SET)?"PERMA":"EPHEM",
+			obj->upload[dataset].packets, obj->upload[dataset].bytes);
+	CDU_DEBUG("Updated %s client stats download packets=%llu bytes %llu\n",
+			(persist == CDU_DB_UPDATE_PERMA_SET)?"PERMA":"EPHEM",
+			obj->download[dataset].packets, obj->download[dataset].bytes);
+
+update_exit:
+	spin_unlock_bh(&cdu_db_hash_lock);
+}
+
+
+void cdu_db_clear_stats(struct usage_entry *obj, bool persist)
+{
+	int dataset = STATSET(persist);
+
+#ifdef ZERO_EPHEM_FLOWS
+	if (dataset) {
+		/* resetting ephemeral, also reset our 'cleared' total */
+		obj->upload[CLEARED_COUNTER].bytes =
+		obj->upload[CLEARED_COUNTER].packets =
+		obj->download[CLEARED_COUNTER].bytes =
+		obj->download[CLEARED_COUNTER].packets = 0;
+	}
+#endif
+	obj->upload[dataset].bytes =
+	obj->upload[dataset].packets =
+	obj->download[dataset].bytes =
+	obj->download[dataset].packets = 0;
+}
+
+
+void cdu_db_get_stats(struct usage_stats *upload, struct usage_stats *download, const struct usage_entry *obj)
+{
+	int direction; /* direction (upload or download) */
+	struct usage_stats *target[] = {upload, download}; /* targets for the stats, in same order as data store */
+	const struct usage_stats *store[] = {obj->upload, obj->download}; /* data stores, in same order as targets */
+	int dataset; /* two datasets (ephemeral and persistent) to be summed */
+
+	for (direction = 0; direction < 2; direction++) {
+		target[direction]->bytes = target[direction]->packets = 0;
+		for (dataset = 0; dataset < 2; dataset++) {
+			target[direction]->bytes += store[direction][dataset].bytes;
+			target[direction]->packets += store[direction][dataset].packets;
+		}
+#ifdef ZERO_EPHEM_FLOWS
+#ifdef DEBUG_ENABLE
+		if (store[direction][CLEARED_COUNTER].packets) {
+			if (obj->family == NFPROTO_IPV4)
+				CDU_DEBUG("Flow stats for %pI4, subtracting %llu from %llu bytes (%llu from %llu packets)\n",
+					&obj->addr.ip,
+					store[direction][CLEARED_COUNTER].bytes, target[direction]->bytes,
+					store[direction][CLEARED_COUNTER].packets, target[direction]->packets
+					);
+			else
+				CDU_DEBUG("Flow stats for %pI6, subtracting %llu from %llu bytes (%llu from %llu packets)\n",
+					&obj->addr.ip6,
+					store[direction][CLEARED_COUNTER].bytes, target[direction]->bytes,
+					store[direction][CLEARED_COUNTER].packets, target[direction]->packets
+					);
+		}
+#endif
+		/* subtract counts from cleared partial sessions
+		 * since the counts are from previous partial sessions,
+		 * they will be less than or equal to the new counts
+		 */
+		target[direction]->bytes -= store[direction][CLEARED_COUNTER].bytes;
+		target[direction]->packets -= store[direction][CLEARED_COUNTER].packets;
+#endif
+	}
+}
+
+
+void cdu_db_read_conn(bool clear)
+{
+	struct nf_conntrack_tuple_hash *h;
+	const struct hlist_nulls_node *nn;
+	struct nf_conn *ct_iter;
+	int i;
+
+	local_bh_disable();
+	for (i = 0; i < init_net.ct.htable_size; i++) {
+		spin_lock(&nf_conntrack_locks[i % CONNTRACK_LOCKS]);
+		if (i < init_net.ct.htable_size) {
+			hlist_nulls_for_each_entry(h, nn, &init_net.ct.hash[i], hnnode) {
+				if (NF_CT_DIRECTION(h) == IP_CT_DIR_ORIGINAL) {
+					ct_iter = nf_ct_tuplehash_to_ctrack(h);
+					if (ct_iter->orig_direction != CDU_DIR_NOT_SET) {
+						CDU_DEBUG("ct iter %p (bucket: %d)\n", ct_iter, i);
+						cdu_db_update_entry(ct_iter, CDU_DB_UPDATE_EPHEM_SET, clear);
+					}
+				}
+			}
+		}
+		spin_unlock(&nf_conntrack_locks[i % CONNTRACK_LOCKS]);
+	}
+	local_bh_enable();
+}
+
+
+inline void cdu_db_remove(struct usage_entry *obj)
+{
+	cdu_db_usage_count--;
+	hlist_del(&obj->node);
+	list_del(&obj->lnode);
+	kfree(obj);
+}
+
+void cdu_db_flush(void)
+{
+	struct usage_entry *obj;
+	struct hlist_node *n;
+	int i = 0;
+
+	for (i = 0; i < USAGE_HASH_BITS; i++) {
+		spin_lock_bh(&cdu_db_hash_lock);
+		if (!hlist_empty(&cdu_db_usage_hash[i])) {
+			hlist_for_each_entry_safe(obj, n, &cdu_db_usage_hash[i], node) {
+				cdu_db_remove(obj);
+			}
+		}
+		spin_unlock_bh(&cdu_db_hash_lock);
+	}
+}
+
+void cdu_db_uninit(void)
+{
+	cdu_db_flush();
+}
+
+int cdu_db_init(void)
+{
+	int i;
+
+	/* initialize empty hashtable buckets */
+	for (i = 0; i < USAGE_HASH_BITS; i++)
+		INIT_HLIST_HEAD(&cdu_db_usage_hash[i]);
+
+	INIT_LIST_HEAD(&cdu_db_usage_list);
+
+	get_random_bytes(&jhash_rnd, sizeof(jhash_rnd));
+
+	CDU_DEBUG("db initialized\n");
+
+	return 0;
+}
+
diff --git a/client_data_usage_brcm_mtk/cdu_db.h b/client_data_usage_brcm_mtk/cdu_db.h
new file mode 100644
index 0000000..131670f
--- /dev/null
+++ b/client_data_usage_brcm_mtk/cdu_db.h
@@ -0,0 +1,41 @@
+/* cdu_db.h
+ *
+ * This file defines client data usage database interface.
+ *
+ * Author: Cradlepoint Technology, Inc.  <source@cradlepoint.com>
+ *		Adrian Sitterle <asitterle@cradlepoint.com>
+ *
+ * Copyright (C) 2019 Cradlepoint Technology, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#pragma once
+#include "cdu_types.h"
+
+
+#define CDU_DB_UPDATE_PERMA_SET 1
+#define CDU_DB_UPDATE_EPHEM_SET 0
+#define CDU_DB_CONNTR_NOCLEAR 0
+
+extern struct hlist_head cdu_db_usage_hash[USAGE_HASH_BITS] __read_mostly;
+extern struct list_head cdu_db_usage_list;
+extern spinlock_t cdu_db_hash_lock;
+
+int cdu_db_init(void);
+void cdu_db_uninit(void);
+void cdu_db_remove(struct usage_entry *obj);
+void cdu_db_flush(void);
+void cdu_db_update_entry(const struct nf_conn *ct, bool persist, bool clear);
+void cdu_db_clear_stats(struct usage_entry *obj, bool persist);
+void cdu_db_get_stats(struct usage_stats *upload, struct usage_stats *download, const struct usage_entry *obj);
+void cdu_db_add_entry_ifmissing(const struct nf_conn *ct);
+
+void cdu_db_read_conn(bool clear);
diff --git a/client_data_usage_brcm_mtk/cdu_init.c b/client_data_usage_brcm_mtk/cdu_init.c
new file mode 100644
index 0000000..f5106ad
--- /dev/null
+++ b/client_data_usage_brcm_mtk/cdu_init.c
@@ -0,0 +1,90 @@
+/* cdu_init.c
+ *
+ * This file defines client data usage module init.
+ *
+ * Author: Cradlepoint Technology, Inc.  <source@cradlepoint.com>
+ *		Adrian Sitterle <asitterle@cradlepoint.com>
+ *
+ * Copyright (C) 2019 Cradlepoint Technology, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/version.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/jhash.h>
+#include <net/netfilter/nf_conntrack.h>
+#include <net/netfilter/nf_conntrack_core.h>
+#include <net/netfilter/nf_conntrack_acct.h>
+
+#include "cdu_types.h"
+#include "cdu_db.h"
+#include "cdu_nf_events.h"
+#include "cdu_xt_target.h"
+#include "cdu_seq_file.h"
+
+
+int __init usage_init_module(void)
+{
+	int ret;
+
+	CDU_DEBUG("Initializing module.\n");
+
+	/* initialize usage_hash database */
+	cdu_db_init();
+
+	/* initialize proc file interface */
+	if (cdu_seq_file_init())
+		goto err_seq;
+
+	/* initialize netfilter eventing triggered at beginning and end of a flow */
+	ret = cdu_nf_events_register();
+	if (ret)
+		goto err_nf;
+
+	/* initialize xt table callbacks, used to determine direction of ct */
+	ret = cdu_xt_target_register();
+	if (ret)
+		goto err_xt;
+
+	CDU_INFO("Client data usage module loaded.\n");
+	return 0;
+
+err_xt:
+	cdu_xt_target_unregister();
+err_nf:
+	cdu_nf_events_unregister();
+err_seq:
+	cdu_seq_file_uninit();
+
+	cdu_db_uninit();
+
+	CDU_INFO("Client data usage module init failed. %d\n", ret);
+	return ret;
+
+}
+
+void __exit usage_exit_module(void)
+{
+	cdu_xt_target_unregister();
+	cdu_nf_events_unregister();
+	cdu_seq_file_uninit();
+	cdu_db_uninit();
+	CDU_INFO("Client data usage module unloaded.\n");
+}
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Cradlepoint, Inc");
+MODULE_DESCRIPTION("Client usage statistics");
+
+module_init(usage_init_module);
+module_exit(usage_exit_module);
diff --git a/client_data_usage_brcm_mtk/cdu_nf_events.c b/client_data_usage_brcm_mtk/cdu_nf_events.c
new file mode 100644
index 0000000..ef66472
--- /dev/null
+++ b/client_data_usage_brcm_mtk/cdu_nf_events.c
@@ -0,0 +1,96 @@
+/* cdu_nf_events.c
+ *
+ * This file handles client data usage netfilter conntrack events.
+ *
+ * Author: Cradlepoint Technology, Inc.  <source@cradlepoint.com>
+ *		Adrian Sitterle <asitterle@cradlepoint.com>
+ *
+ * Copyright (C) 2019 Cradlepoint Technology, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/version.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <net/netfilter/nf_conntrack.h>
+#include <net/netfilter/nf_conntrack_core.h>
+
+#include "cdu_db.h"
+#include "cdu_nf_events.h"
+#include "cdu_xt_target.h"
+#include "cdu_types.h"
+
+#ifndef CONFIG_NF_CONNTRACK_CHAIN_EVENTS
+#error Can not use this module without conntrack events
+#endif
+
+static u_int8_t nf_registered;
+
+static int cdu_nf_event(struct notifier_block *this, unsigned long events, void *ptr)
+{
+	struct nf_ct_event *item = (struct nf_ct_event *)ptr;
+	struct nf_conn *ct = item->ct;
+
+	if (!ct) {
+		CDU_DEBUG("No ct available\n");
+		return -EINVAL;
+	}
+
+	if (ct->orig_direction == CDU_DIR_NOT_SET) {
+		/* This ct does not have a direction information because it didn't
+		 * travel through the xt hook, which means that we don't care
+		 * about this traffic
+		 */
+		return NOTIFY_DONE;
+	}
+
+	/* Update data usage htable entry on end of flow trigger */
+	if (events & (1 << IPCT_DESTROY)) {
+		CDU_DEBUG("Caught %s NF event. mac=%pM, dir %s, ct %p\n",
+			events & (1 << IPCT_NEW)?"NEW":"DESTROY", ct->mac,
+			(ct->orig_direction == CDU_DIR_SRC)?"DIR_SRC":"DIR_DEST", ct);
+
+		cdu_db_update_entry(ct, CDU_DB_UPDATE_PERMA_SET, CDU_DB_CONNTR_NOCLEAR);
+	}
+
+	return NOTIFY_DONE;
+}
+
+static struct notifier_block cdu_nf_notifier = {
+	.notifier_call	= cdu_nf_event,
+};
+
+int cdu_nf_events_register(void)
+{
+	int result;
+
+	if (!nf_registered) {
+		result = nf_conntrack_register_notifier(&init_net, &cdu_nf_notifier);
+		if (result < 0) {
+			CDU_INFO("Failed to register conntrack notifier %d\n", result);
+			return result;
+		}
+		nf_registered = 1;
+	}
+
+	CDU_DEBUG("initialized\n");
+	return 0;
+}
+
+void cdu_nf_events_unregister(void)
+{
+	if (nf_registered) {
+		nf_conntrack_unregister_notifier(&init_net, &cdu_nf_notifier);
+		CDU_DEBUG("uninitialized\n");
+	}
+}
+
diff --git a/client_data_usage_brcm_mtk/cdu_nf_events.h b/client_data_usage_brcm_mtk/cdu_nf_events.h
new file mode 100644
index 0000000..33bc4b9
--- /dev/null
+++ b/client_data_usage_brcm_mtk/cdu_nf_events.h
@@ -0,0 +1,22 @@
+/* cdu_nf_events.h
+ *
+ * This file defines client data usage netfilter events interface.
+ *
+ * Author: Cradlepoint Technology, Inc.  <source@cradlepoint.com>
+ *		Adrian Sitterle <asitterle@cradlepoint.com>
+ *
+ * Copyright (C) 2019 Cradlepoint Technology, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+int cdu_nf_events_register(void);
+void cdu_nf_events_unregister(void);
+
diff --git a/client_data_usage_brcm_mtk/cdu_seq_file.c b/client_data_usage_brcm_mtk/cdu_seq_file.c
new file mode 100644
index 0000000..30fcaf7
--- /dev/null
+++ b/client_data_usage_brcm_mtk/cdu_seq_file.c
@@ -0,0 +1,225 @@
+/* cdu_seq_file.c
+ *
+ * This file defines client data usage proc file stats reporting.
+ *
+ * Author: Cradlepoint Technology, Inc.  <source@cradlepoint.com>
+ *		Adrian Sitterle <asitterle@cradlepoint.com>
+ *
+ * Copyright (C) 2019 Cradlepoint Technology, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/version.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/jhash.h>
+#include <linux/etherdevice.h>
+#include <linux/proc_fs.h>
+#include <net/netfilter/nf_conntrack.h>
+#include <net/netfilter/nf_conntrack_core.h>
+
+#include "cdu_seq_file.h"
+#include "cdu_db.h"
+
+
+static void *seq_start(struct seq_file *s, loff_t *pos)
+{
+	unsigned int *bucket;
+
+	spin_lock_bh(&cdu_db_hash_lock);
+	if (*pos >= USAGE_HASH_BITS)
+		return NULL;
+
+	bucket = kmalloc(sizeof(unsigned int), GFP_ATOMIC);
+	if (!bucket)
+		return ERR_PTR(-ENOMEM);
+
+	*bucket = *pos;
+
+	return bucket;
+}
+
+
+static void *seq_next(struct seq_file *s, void *v, loff_t *pos)
+{
+	unsigned int *bucket = (unsigned int *)v;
+
+	*pos = ++(*bucket);
+	if (*pos >= USAGE_HASH_BITS) {
+		kfree(v);
+		return NULL;
+	}
+
+	return bucket;
+}
+
+
+static void seq_stop(struct seq_file *s, void *v)
+{
+	unsigned int *bucket = (unsigned int *)v;
+
+	if (!IS_ERR_OR_NULL(bucket))
+		kfree(bucket);
+	spin_unlock_bh(&cdu_db_hash_lock);
+
+}
+
+
+#define CDU_DEBUGIMEOUT 300
+static int seq_show(struct seq_file *s, void *v)
+{
+	struct usage_htable *htable = s->private;
+	unsigned int *bucket = (unsigned int *)v;
+	struct usage_entry *obj;
+	struct hlist_node *n;
+	struct timespec now;
+	struct usage_stats upload, download;
+
+	if (!hlist_empty(&cdu_db_usage_hash[*bucket])) {
+		get_monotonic_boottime(&now);
+
+		hlist_for_each_entry_safe(obj, n, &cdu_db_usage_hash[*bucket], node) {
+			cdu_db_get_stats(&upload, &download, obj);
+			CDU_DEBUG(
+				"MAC = %pm, IP = %pI4, IP6 = %pI6, Up_Bytes = %llu, Up_Packets = %llu, Down_Bytes = %llu, Down_Packets = %llu, Last_Time = %lu, First_Time = %lu, Connect_Time = %lu, Timeout = %s\n",
+					obj->mac, &obj->addr.ip, &obj->addr.ip6,
+					upload.bytes, upload.packets,
+					download.bytes, download.packets,
+					obj->last_time, obj->first_time, obj->last_time - obj->first_time,
+					(CDU_DEBUGIMEOUT + obj->last_time) < (unsigned long) now.tv_sec ? "True" : "False");
+
+			seq_printf(s, "MAC = %pM, ", obj->mac);
+
+			if (obj->family == NFPROTO_IPV4)
+				seq_printf(s, "IP = %pI4, ", &obj->addr.ip);
+
+			if (obj->family == NFPROTO_IPV6)
+				seq_printf(s, "IP = %pI6, ", &obj->addr.ip6);
+
+			seq_printf(s, "Up_Bytes = %llu, Up_Packets = %llu, ", upload.bytes, upload.packets);
+			seq_printf(s, "Down_Bytes = %llu, Down_Packets = %llu, ", download.bytes, download.packets);
+
+			seq_printf(s, "Last_Time = %lu, First_Time = %lu, Connect_Time = %lu, ",
+					obj->last_time, obj->first_time, obj->last_time - obj->first_time);
+
+			seq_printf(s, "Timeout = %s\n", (CDU_DEBUGIMEOUT + obj->last_time) < (unsigned long) now.tv_sec ? "True" : "False");
+
+			if (htable->reset == 1) {
+				if ((CDU_DEBUGIMEOUT + obj->last_time) < (unsigned long) now.tv_sec) {
+					cdu_db_remove(obj);
+				} else {
+					cdu_db_clear_stats(obj, CDU_DB_UPDATE_PERMA_SET);
+					cdu_db_clear_stats(obj, CDU_DB_UPDATE_EPHEM_SET);
+				}
+			} else {
+				cdu_db_clear_stats(obj, CDU_DB_UPDATE_EPHEM_SET);
+			}
+		}
+	}
+
+	return 0;
+}
+
+
+static const struct seq_operations usage_seq_ops = {
+	.start	= seq_start,
+	.next	= seq_next,
+	.stop	= seq_stop,
+	.show	= seq_show,
+};
+
+
+static int usage_open(struct inode *inode, struct file *file)
+{
+	struct seq_file *s;
+	int ret = seq_open(file, &usage_seq_ops);
+
+	if (!ret) {
+		s = file->private_data;
+		s->private = PDE_DATA(inode);
+		cdu_db_read_conn(((struct usage_htable *)(s->private))->reset);
+	}
+
+	return ret;
+}
+
+static const struct file_operations usage_file_ops = {
+	.owner	 = THIS_MODULE,
+	.open	 = usage_open,
+	.read	 = seq_read,
+	.llseek	 = seq_lseek,
+	.release = seq_release,
+};
+
+static struct proc_dir_entry *ipt_usage_procdir;
+
+void cdu_seq_file_uninit(void)
+{
+	if (ipt_usage_procdir) {
+		proc_remove(ipt_usage_procdir);
+		ipt_usage_procdir = NULL;
+	}
+}
+
+struct usage_htable *cdu_seq_file_create(const char *name, const u_int8_t readreset)
+{
+	struct usage_htable *priv;
+
+	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+	if (priv == NULL)
+		return NULL;
+
+	priv->usecount = 0;
+	priv->reset = readreset == 1;
+
+	priv->name = kstrdup(name, GFP_KERNEL);
+	if (!priv->name) {
+		kfree(priv);
+		return NULL;
+	}
+
+	priv->pde = proc_create_data(name, 0600, ipt_usage_procdir, &usage_file_ops, priv);
+	if (priv->pde == NULL) {
+		kfree(priv->name);
+		kfree(priv);
+		return NULL;
+	}
+
+	return priv;
+}
+
+
+void cdu_seq_file_destroy(const struct usage_htable *priv)
+{
+	/* if destroy is called, empty the list */
+	cdu_db_flush();
+
+	if (priv) {
+		kfree(priv->name);
+
+		if (priv->pde)
+			proc_remove(priv->pde);
+
+		kfree(priv);
+	}
+}
+
+
+int cdu_seq_file_init(void)
+{
+	ipt_usage_procdir = proc_mkdir("xt_usage", init_net.proc_net);
+	if (!ipt_usage_procdir) {
+		CDU_ERROR("proc_mkdir failed.\n");
+		return -ENOMEM;
+	}
+	return 0;
+}
diff --git a/client_data_usage_brcm_mtk/cdu_seq_file.h b/client_data_usage_brcm_mtk/cdu_seq_file.h
new file mode 100644
index 0000000..41602b5
--- /dev/null
+++ b/client_data_usage_brcm_mtk/cdu_seq_file.h
@@ -0,0 +1,27 @@
+/* cdu_seq_file.h
+ *
+ * This file defines client data usage proc file interface.
+ *
+ * Author: Cradlepoint Technology, Inc.  <source@cradlepoint.com>
+ *		Adrian Sitterle <asitterle@cradlepoint.com>
+ *
+ * Copyright (C) 2019 Cradlepoint Technology, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#pragma once
+#include "cdu_types.h"
+
+int cdu_seq_file_init(void);
+void cdu_seq_file_uninit(void);
+
+struct usage_htable *cdu_seq_file_create(const char *name, const u_int8_t readreset);
+void cdu_seq_file_destroy(const struct usage_htable *priv);
diff --git a/client_data_usage_brcm_mtk/cdu_types.h b/client_data_usage_brcm_mtk/cdu_types.h
new file mode 100644
index 0000000..e920c5e
--- /dev/null
+++ b/client_data_usage_brcm_mtk/cdu_types.h
@@ -0,0 +1,90 @@
+/* cdu_types.h
+ *
+ * This file defines client data usage types
+ *
+ * Author: Cradlepoint Technology, Inc.  <source@cradlepoint.com>
+ *		Adrian Sitterle <asitterle@cradlepoint.com>
+ *
+ * Copyright (C) 2019 Cradlepoint Technology, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#pragma once
+
+#include <linux/list.h>
+/* backwards compatible - remote code does not expect cumulative entries, but
+ * instead expects only new data since last cleared
+ * also see kernel nf_conn_counter counter: include/net/netfilter/nf_conntrack_acct.h
+ */
+#define ZERO_EPHEM_FLOWS
+
+#undef DEBUG_ENABLE
+
+#ifdef DEBUG_ENABLE
+#define CDU_DEBUG(s, ...) pr_info_ratelimited("%s[%d]:" s, __func__, __LINE__, ##__VA_ARGS__)
+#else
+#define CDU_DEBUG(s, ...)
+#endif
+
+#define CDU_INFO(s, ...) pr_info_ratelimited("client data usage: " s, ##__VA_ARGS__)
+#define CDU_ERROR(s, ...) pr_err_ratelimited("client data usage: " s, ##__VA_ARGS__)
+
+#ifdef CONFIG_X86_64
+#define CDU_USAGE_MAXENTRY 4096
+#define USAGE_HASH_BITS		128
+#else
+#define CDU_USAGE_MAXENTRY 2048
+#define USAGE_HASH_BITS		32
+#endif
+
+#define CDU_USAGE_DROP_CHUNK	((int) (CDU_USAGE_MAXENTRY / 10)) /* Drop 10 percent of the least recently seen clients */
+#define IP_ALEN 4
+#define IP6_ALEN 16
+
+struct usage_stats {
+	u_int64_t bytes;
+	u_int64_t packets;
+};
+
+/* two datasets: ephemeral for current flows and persistent for finished flows */
+#define STATSET(persist) ((persist) ? 0 : 1)
+
+struct usage_entry {
+	struct hlist_node node;
+	struct list_head lnode;
+	unsigned int key;		/* hash table key */
+
+	union nf_inet_addr addr;	/* client IP addr */
+	unsigned char family;		/* IP proptocol version */
+	unsigned char mac[ETH_ALEN];	/* client MAC addr */
+
+#ifdef ZERO_EPHEM_FLOWS
+#define CLEARED_COUNTER 2
+	struct usage_stats upload[3];	/* 0: persistent set, 1: ephemeral set, 2: cleared counts */
+	struct usage_stats download[3];
+#else
+	struct usage_stats upload[2];	/* 0: persistent set, 1: ephemeral set */
+	struct usage_stats download[2];
+#endif
+
+	unsigned long first_time;
+	unsigned long last_time;
+};
+
+/* because struct xt_usage_tginfo in xt_usage.h */
+#define usage_htable xt_usage_htable
+struct xt_usage_htable {
+	u_int8_t reset;
+	u_int8_t usecount;
+
+	struct proc_dir_entry *pde;
+	char *name;
+};
diff --git a/client_data_usage_brcm_mtk/cdu_xt_target.c b/client_data_usage_brcm_mtk/cdu_xt_target.c
new file mode 100644
index 0000000..3ce181b
--- /dev/null
+++ b/client_data_usage_brcm_mtk/cdu_xt_target.c
@@ -0,0 +1,130 @@
+/* cdu_xt_target.c
+ *
+ * This file defines client data usage xtables target.
+ *
+ * Author: Cradlepoint Technology, Inc.  <source@cradlepoint.com>
+ *		Adrian Sitterle <asitterle@cradlepoint.com>
+ *
+ * Copyright (C) 2019 Cradlepoint Technology, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/version.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/netfilter/xt_usage.h>
+#include <linux/netfilter/x_tables.h>
+#include <net/netfilter/nf_conntrack.h>
+#include <net/netfilter/nf_conntrack_core.h>
+#include <linux/netfilter/nf_conntrack_common.h>
+#include <linux/etherdevice.h>
+
+
+#include "cdu_types.h"
+#include "cdu_xt_target.h"
+#include "cdu_nf_events.h"
+#include "cdu_seq_file.h"
+#include "cdu_db.h"
+
+static unsigned int usage_tg(struct sk_buff *skb, const struct xt_action_param *par)
+{
+	struct nf_conn *ct;
+	enum ip_conntrack_info ctinfo;
+	const struct xt_usage_tginfo *info = par->targinfo;
+	unsigned char direction = (info->src == 1);
+
+	ct = nf_ct_get(skb, &ctinfo);
+	if (unlikely(!ct))
+		return XT_CONTINUE;
+
+	if (likely(ct->orig_direction != CDU_DIR_NOT_SET))
+		return XT_CONTINUE;
+
+	if (direction) {
+
+		if (ctinfo == IP_CT_NEW)
+			ct->orig_direction = CDU_DIR_SRC;
+		else if (ctinfo == IP_CT_IS_REPLY)
+			ct->orig_direction = CDU_DIR_DEST;
+		else
+			return XT_CONTINUE;
+
+		ether_addr_copy(ct->mac, eth_hdr(skb)->h_source);
+
+		cdu_db_add_entry_ifmissing(ct);
+	}
+
+
+	return XT_CONTINUE;
+}
+
+static int xt_usage_check(const struct xt_tgchk_param *par)
+{
+	struct xt_usage_tginfo *info = par->targinfo;
+
+	if (!info->priv) {
+		info->priv = cdu_seq_file_create(info->name, info->readreset);
+		if (!info->priv)
+			return -ENOMEM;
+		cdu_nf_events_register();
+	}
+
+	(info->priv->usecount)++;
+
+	return 0;
+}
+
+static void xt_usage_destroy(const struct xt_tgdtor_param *par)
+{
+	struct xt_usage_tginfo *info = par->targinfo;
+
+	if (info->priv) {
+		CDU_DEBUG("usecount=%u\n", info->priv->usecount);
+		(info->priv->usecount)--;
+		if (info->priv->usecount <= 0) {
+			cdu_seq_file_destroy(info->priv);
+			info->priv = NULL;
+			cdu_nf_events_unregister();
+		}
+	}
+}
+
+static struct xt_target usage_tg_reg __read_mostly = {
+	.name      = "USAGE",
+	.revision  = 0,
+	.family    = NFPROTO_UNSPEC,
+	.target    = usage_tg,
+	.checkentry = xt_usage_check,
+	.destroy   = xt_usage_destroy,
+	.targetsize = sizeof(struct xt_usage_tginfo),
+	.hooks     = (1 << NF_INET_PRE_ROUTING) | (1 << NF_INET_LOCAL_IN) |
+		     (1 << NF_INET_FORWARD) | (1 << NF_INET_POST_ROUTING) |
+		     (1 << NF_INET_LOCAL_OUT),
+	.me        = THIS_MODULE,
+};
+
+int cdu_xt_target_register(void)
+{
+	int result = xt_register_target(&usage_tg_reg);
+
+	if (result < 0) {
+		CDU_INFO("Failed to register xt target notifier %d\n", result);
+		return result;
+	}
+	CDU_DEBUG("initialized\n");
+	return 0;
+}
+
+void cdu_xt_target_unregister(void)
+{
+	xt_unregister_target(&usage_tg_reg);
+	CDU_DEBUG("uninitialized\n");
+}
diff --git a/client_data_usage_brcm_mtk/cdu_xt_target.h b/client_data_usage_brcm_mtk/cdu_xt_target.h
new file mode 100644
index 0000000..8ea6534
--- /dev/null
+++ b/client_data_usage_brcm_mtk/cdu_xt_target.h
@@ -0,0 +1,27 @@
+/* cdu_xt_target.h
+ *
+ * This file defines client data usage xtables target interface
+ *
+ * Author: Cradlepoint Technology, Inc.  <source@cradlepoint.com>
+ *		Adrian Sitterle <asitterle@cradlepoint.com>
+ *
+ * Copyright (C) 2019 Cradlepoint Technology, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+enum {
+	CDU_DIR_NOT_SET,
+	CDU_DIR_SRC,
+	CDU_DIR_DEST
+};
+
+int cdu_xt_target_register(void);
+void cdu_xt_target_unregister(void);