[ipq806x] Initial commit of NSS IPV4 connection manager

Following changes were made w.r.t IP8k code
[1] Fixed name-space across the code
[2] Connection table is now moved to connection manager from NSS driver.
    Connection manager updates this table on sync/establish messages
    from NSS driver.
[3] Added debugfs support to read and clear Ipv4 stats
[4] Ported ARP table update code from IP8K code.
[5] Added IPv6 Connection Manager

Change-Id: I9041d1fcb9b6ff8e98a78f7982ccefbd3e84fb43
Signed-off-by: Pamidipati, Vijay <vpamidip@codeaurora.org>
Reviewed-by: Bob Amstadt <ramstadt@codeaurora.org>
diff --git a/Makefile b/Makefile
index 2f92b92..d986003 100755
--- a/Makefile
+++ b/Makefile
@@ -1,9 +1,28 @@
+#
+# Copyright (c) 2013 Qualcomm Atheros, Inc..
+# All Rights Reserved.
+#
+# -----------------------------REVISION HISTORY---------------------------------
+# Qualcomm Atheros                01/Feb/2013                  Created
+#
+
+# ###################################################
+# # Makefile for the NSS driver
+# ###################################################
+
 obj-m += qca-nss-drv.o
 qca-nss-drv-objs := nss_init.o nss_core.o nss_tx_rx.o nss_stats.o
 
-obj ?= .
+obj-m += qca-nss-connmgr-ipv4.o
+obj-m += qca-nss-connmgr-ipv6.o
+
+qca-nss-connmgr-ipv4-objs := nss_connmgr_ipv4.o
+qca-nss-connmgr-ipv6-objs := nss_connmgr_ipv6.o
 
 ccflags-y += -I$(obj)/nss_hal/include -DNSS_DEBUG_LEVEL=0 -DNSS_EMPTY_BUFFER_SIZE=1792 -DNSS_PKT_STATS_ENABLED=0
+ccflags-y += -DNSS_CONNMGR_DEBUG_LEVEL=0
+
+obj ?= .
 
 ifeq "$(CONFIG_ARCH_IPQ806X)" "y"
 qca-nss-drv-objs += nss_hal/ipq806x/nss_hal_pvt.o
diff --git a/nss_connmgr_ipv4.c b/nss_connmgr_ipv4.c
new file mode 100755
index 0000000..dd52f9e
--- /dev/null
+++ b/nss_connmgr_ipv4.c
@@ -0,0 +1,1652 @@
+/* * Copyright (c) 2013 Qualcomm Atheros, Inc. * */
+
+/*
+ * nss_connmgr_ipv4.c
+ *
+ * This file is the NSS connection manager for managing IPv4 connections.It
+ * forms an interface between the fast-path NSS driver and Linux
+ * connection track module for updating/syncing connection level information
+ * between the two.It is responsible for maintaing  all connection (flow) level
+ * information and statistics for all fast path connections.
+ *
+ * ------------------------REVISION HISTORY-----------------------------
+ * Qualcomm Atheros         01/Mar/2013              Created
+ */
+
+#include <linux/types.h>
+#include <linux/ip.h>
+#include <linux/tcp.h>
+#include <linux/module.h>
+#include <linux/skbuff.h>
+#include <linux/icmp.h>
+#include <linux/sysctl.h>
+#include <linux/kthread.h>
+#include <linux/fs.h>
+#include <linux/pkt_sched.h>
+#include <linux/string.h>
+#include <linux/debugfs.h>
+#include <linux/netdevice.h>
+
+#include <net/route.h>
+#include <net/ip.h>
+#include <net/tcp.h>
+#include <asm/unaligned.h>
+#include <asm/uaccess.h>	/* for put_user */
+
+#include <linux/netfilter_ipv4.h>
+#include <linux/netfilter_bridge.h>
+#include <linux/if_bridge.h>
+#include <net/netfilter/nf_conntrack.h>
+#include <net/netfilter/nf_conntrack_acct.h>
+#include <net/netfilter/nf_conntrack_helper.h>
+#include <net/netfilter/nf_conntrack_l4proto.h>
+#include <net/netfilter/nf_conntrack_l3proto.h>
+#include <net/netfilter/nf_conntrack_zones.h>
+#include <net/netfilter/nf_conntrack_core.h>
+#include <net/netfilter/ipv4/nf_conntrack_ipv4.h>
+#include <net/netfilter/ipv4/nf_defrag_ipv4.h>
+
+#include <net/arp.h>
+
+#include "nss_api_if.h"
+
+/*
+ * Debug output levels
+ * 0 = OFF
+ * 1 = ASSERTS / ERRORS
+ * 2 = 1 + WARN
+ * 3 = 2 + INFO
+ * 4 = 3 + TRACE
+ */
+
+#if (NSS_CONNMGR_DEBUG_LEVEL < 1)
+#define NSS_CONNMGR_DEBUG_ASSERT(s, ...)
+#define NSS_CONNMGR_DEBUG_ERROR(s, ...)
+#define NSS_CONNMGR_DEBUG_CHECK_MAGIC(i, m, s, ...)
+#define NSS_CONNMGR_DEBUG_SET_MAGIC(i, m)
+#define NSS_CONNMGR_DEBUG_CLEAR_MAGIC(i)
+#else
+#define NSS_CONNMGR_DEBUG_ASSERT(c, s, ...) if (!(c)) { printk("%s:%d:ASSERT:", __FILE__, __LINE__);printk(s, ##__VA_ARGS__); while(1); }
+#define NSS_CONNMGR_DEBUG_ERROR(s, ...) printk("%s:%d:ERROR:", __FILE__, __LINE__);printk(s, ##__VA_ARGS__)
+#define NSS_CONNMGR_DEBUG_CHECK_MAGIC(i, m, s, ...) if (i->magic != m) { NSS_CONNMGR_DEBUG_ASSERT(FALSE, s, ##__VA_ARGS__); }
+#define NSS_CONNMGR_DEBUG_SET_MAGIC(i, m) i->magic = m
+#define NSS_CONNMGR_DEBUG_CLEAR_MAGIC(i) i->magic = 0
+#endif
+
+#if (NSS_CONNMGR_DEBUG_LEVEL < 2)
+#define NSS_CONNMGR_DEBUG_WARN(s, ...)
+#else
+#define NSS_CONNMGR_DEBUG_WARN(s, ...) { printk("%s:%d:WARN:", __FILE__, __LINE__);printk(s, ##__VA_ARGS__); }
+#endif
+
+#if (NSS_CONNMGR_DEBUG_LEVEL < 3)
+#define NSS_CONNMGR_DEBUG_INFO(s, ...)
+#else
+#define NSS_CONNMGR_DEBUG_INFO(s, ...) { printk("%s:%d:INFO:", __FILE__, __LINE__);printk(s, ##__VA_ARGS__); }
+#endif
+
+#if (NSS_CONNMGR_DEBUG_LEVEL < 4)
+#define NSS_CONNMGR_DEBUG_TRACE(s, ...)
+#else
+#define NSS_CONNMGR_DEBUG_TRACE(s, ...) { printk("%s:%d:TRACE:", __FILE__, __LINE__);printk(s, ##__VA_ARGS__); }
+#endif
+
+/*
+ * Custom types recognised within the Connection Manager
+ */
+typedef uint8_t mac_addr_t[6];
+typedef uint32_t ipv4_addr_t;
+
+/*
+ * Tuple Match
+ */
+#define IS_TUPLE_MATCH( connection , sync) (\
+		(connection->src_addr == establish->flow_ip) && \
+		(connection->src_port == establish->flow_ident) && \
+		(connection->src_addr_xlate == establish->flow_ip_xlate) && \
+		(connection->src_port_xlate == establish->flow_ident_xlate) && \
+		(connection->dest_addr == establish->return_ip) && \
+		(connection->dest_port == establish->return_ident) && \
+		(connection->dest_addr_xlate == establish->return_ip_xlate) && \
+		(connection->dest_port_xlate == establish->return_ident_xlate))
+
+/*
+ * Local Host IP = 127.0.0.1 (0x7f:00:00:01)
+ */
+#define IS_LOCAL_HOST(ip) (ip == 0x7f000001)
+
+/*
+ * Displaying addresses
+ */
+#define MAC_AS_BYTES(mac_addr) mac_addr[5], mac_addr[4], mac_addr[3], mac_addr[2], mac_addr[1], mac_addr[0]
+
+#define MAC_FMT "%02x:%02x:%02x:%02x:%02x:%02x"
+
+#define IPV4_ADDR_FMT "%d.%d.%d.%d"
+
+#define IPV4_ADDR_TO_QUAD(ip)  (ip) >> 24, ((ip) & 0x00ff0000) >> 16, ((ip) & 0x0000ff00) >> 8, ((ip) & 0x000000ff)
+
+/*
+ * Max NSS IPV4 Flow entries
+ *
+ * TODO - This information should come from NSS. Once NSS driver team adds an
+ * API for this, change this.
+ */
+#define NSS_CONNMGR_IPV4_CONN_MAX 256
+
+/*
+ * size of buffer allocated for stats printing (using debugfs)
+ */
+#define NSS_CONNMGR_IPV4_DEBUGFS_BUF_SZ (NSS_CONNMGR_IPV4_CONN_MAX*512)
+
+/*
+ * Maximum string length:
+ * This should be equal to maximum string size of any stats
+ * inclusive of stats value
+ */
+#define NSS_CONNMGR_IPV4_MAX_STR_LENGTH 96
+
+
+/*
+ * IPV4 Connection statistics
+ */
+enum nss_connmgr_ipv4_conn_statistics {
+	NSS_CONNMGR_IPV4_ACCELERATED_RX_PKTS = 0,
+	/* Accelerated IPv4 RX packets */
+	NSS_CONNMGR_IPV4_ACCELERATED_RX_BYTES,
+	/* Accelerated IPv4 RX bytes */
+	NSS_CONNMGR_IPV4_ACCELERATED_TX_PKTS,
+	/* Accelerated IPv4 TX packets */
+	NSS_CONNMGR_IPV4_ACCELERATED_TX_BYTES,
+	/* Accelerated IPv4 TX bytes */
+	NSS_CONNMGR_IPV4_STATS_MAX
+};
+
+typedef enum nss_connmgr_ipv4_conn_statistics nss_connmgr_ipv4_conn_statistics_t;
+
+/*
+ * Debug statistics
+ */
+enum nss_connmgr_ipv4_debug_statistics {
+	NSS_CONNMGR_IPV4_CREATE_FAIL,
+				/* Rule create failures */
+	NSS_CONNMGR_IPV4_DESTROY_FAIL,
+				/* Rule destroy failures */
+	NSS_CONNMGR_IPV4_ESTABLISH_MISS,
+				/* No establish response from NSS  */
+	NSS_CONNMGR_IPV4_DESTROY_MISS,
+				/* No Flush/Evict/Destroy response from NSS */
+	NSS_CONNMGR_IPV4_DEBUG_STATS_MAX
+};
+
+typedef enum nss_connmgr_ipv4_debug_statistics nss_connmgr_debug_ipv4_statistics_t;
+
+/*
+ * nss_connmgr_ipv4_conn_stats_str
+ *      Connection statistics strings
+ */
+static char *nss_connmgr_ipv4_conn_stats_str[] = {
+	"rx_pkts",
+	"rx_bytes",
+	"tx_pkts",
+	"tx_bytes",
+};
+
+/*
+ * nss_connmgr_ipv4_stats_str
+ *      Debug statistics strings
+ */
+static char *nss_connmgr_ipv4_debug_stats_str[] = {
+	"create_fail",
+	"destroy_fail",
+	"establish_miss",
+	"destroy_miss",
+};
+
+/*
+ * Connection states as defined by connection manager
+ *
+ * INACTIVE    Connection is not active
+ * ESTABLISHED Connection is established, and NSS sends periodic sync messages
+ * STALE       Linux and NSS are out of sync.
+ *		Conntrack -> Conn Mgr sent a rule destroy command,
+ *		but the command did  not reach NSS
+ *
+ */
+typedef enum  {
+	NSS_CONNMGR_IPV4_STATE_INACTIVE,
+	NSS_CONNMGR_IPV4_STATE_ESTABLISHED,
+	NSS_CONNMGR_IPV4_STATE_STALE,
+} nss_connmgr_ipv4_conn_state_t;
+
+/*
+ * Connection states strings
+ */
+static char *nss_connmgr_ipv4_conn_state_str[] = {
+	"inactive",
+	"established",
+	"stale",
+};
+
+/*
+ * IPv4 connection info
+ */
+struct nss_connmgr_ipv4_connection {
+	nss_connmgr_ipv4_conn_state_t  state;
+					/* Connection state */
+	uint8_t  protocol;		/* Protocol number */
+	int32_t  src_interface;		/* Flow interface number */
+	uint32_t src_addr;		/* Non-NAT source address, i.e. the creator of the connection */
+	int32_t  src_port;		/* Non-NAT source port */
+	uint32_t src_addr_xlate;	/* NAT translated source address, i.e. the creator of the connection */
+	int32_t  src_port_xlate;	/* NAT translated source port */
+	int32_t  dest_interface;	/* Return interface number */
+	uint32_t dest_addr;		/* Non-NAT destination address, i.e. the to whom the connection was created */
+	int32_t  dest_port;		/* Non-NAT destination port */
+	uint32_t dest_addr_xlate;	/* NAT translated destination address, i.e. the to whom the connection was created */
+	int32_t  dest_port_xlate;	/* NAT translated destination port */
+	uint64_t stats[NSS_CONNMGR_IPV4_STATS_MAX];
+					/* Connection statistics */
+	uint32_t last_sync;		/* Last sync time as jiffies */
+};
+
+/*
+ * Global connection manager instance object.
+ */
+struct nss_connmgr_ipv4_instance {
+	spinlock_t lock;		/* Lock to Protect against SMP access. */
+	struct kobject *nom_v4;	/* Sysfs link. Represents the sysfs folder /sys/nom_v4 */
+	int32_t stopped;		/* General operational control.When non-zero further traffic will not be processed */
+	int32_t terminate;		/* Signal to tell the control thread to terminate */
+	struct task_struct *thread;
+					/* Control thread */
+	void *nss_context;		/* Registration context used to identify the manager in calls to the NSS driver */
+#ifdef CONFIG_NF_CONNTRACK_CHAIN_EVENTS
+	struct notifier_block conntrack_notifier;
+					/* NF conntrack event system to monitor connection tracking changes */
+#else
+	struct nf_ct_event_notifier conntrack_notifier;
+					/* NF conntrack event system to monitor connection tracking changes */
+#endif
+	struct nss_connmgr_ipv4_connection connection[NSS_CONNMGR_IPV4_CONN_MAX];
+					/* Connection Table */
+	struct dentry *dent;		/* Debugfs directory */
+	uint32_t debug_stats[NSS_CONNMGR_IPV4_DEBUG_STATS_MAX];
+					/* Debug statistics */
+};
+
+static unsigned int nss_connmgr_ipv4_post_routing_hook(unsigned int hooknum,
+				struct sk_buff *skb,
+				const struct net_device *in_unused,
+				const struct net_device *out,
+				int (*okfn)(struct sk_buff *));
+
+static ssize_t nss_connmgr_ipv4_read_conn_stats(struct file *fp, char __user *ubuf, size_t sz, loff_t *ppos);
+
+static ssize_t nss_connmgr_ipv4_read_debug_stats(struct file *fp, char __user *ubuf, size_t sz, loff_t *ppos);
+
+static ssize_t nss_connmgr_ipv4_clear_stats(struct file *fp, const char __user *ubuf, size_t count, loff_t *ppos);
+
+#ifdef CONFIG_NF_CONNTRACK_CHAIN_EVENTS
+static int nss_connmgr_ipv4_conntrack_event(struct notifier_block *this, unsigned long events, void *ptr);
+#else
+static int nss_connmgr_ipv4_conntrack_event(unsigned int events, struct nf_ct_event *item);
+#endif
+
+
+static struct nss_connmgr_ipv4_instance nss_connmgr_ipv4 = {
+		.stopped = 0,
+		.terminate= 0,
+		.thread = NULL,
+		.nss_context = NULL,
+#ifdef CONFIG_NF_CONNTRACK_CHAIN_EVENTS
+		.conntrack_notifier = {
+			.notifier_call = nss_connmgr_ipv4_conntrack_event,
+		},
+#else
+		.conntrack_notifier = {
+			.fcn = nss_connmgr_ipv4_conntrack_event,
+		},
+#endif
+};
+
+/*
+ * Post routing netfilter hook
+ * 	This will pick up local outbound and packets going from one interface to another
+ */
+static struct nf_hook_ops nss_connmgr_ipv4_ops_post_routing[] __read_mostly = {
+	{
+	.hook           = nss_connmgr_ipv4_post_routing_hook,
+	.owner          = THIS_MODULE,
+	.pf             = PF_INET,
+	.hooknum        = NF_INET_POST_ROUTING,
+	.priority       = NF_IP_PRI_NAT_SRC + 1, /*
+						  * Refer to include/linux/netfiler_ipv4.h for priority levels.
+						  * Examine packets after NAT translation (and potentially any ALG processing)
+						  */
+	},
+};
+
+/*
+ * Expose what should be static flags in the TCP connection tracker.
+ */
+extern int nf_ct_tcp_be_liberal;
+extern int nf_ct_tcp_no_window_check;
+
+static const struct file_operations nss_connmgr_ipv4_show_stats_ops = {
+	.open = simple_open,
+	.read = nss_connmgr_ipv4_read_conn_stats,
+};
+
+static const struct file_operations nss_connmgr_ipv4_show_debug_stats_ops = {
+	.open = simple_open,
+	.read = nss_connmgr_ipv4_read_debug_stats,
+};
+
+static const struct file_operations nss_connmgr_ipv4_clear_stats_ops = {
+	.write = nss_connmgr_ipv4_clear_stats,
+};
+
+/*
+ * Network flow
+ *
+ * HOST1-----------ROUTER------------HOST2
+ * HOST1 has IPa and MACa
+ * ROUTER has IPb and MACb facing HOST1 and IPc and MACc facing HOST2
+ * HOST2 has IPd and MACd
+ * i.e.
+ * HOST1-----------------ROUTER----------------HOST2
+ * IPa/MACa--------IPb/MACb:::IPc/MACc---------IPd/MACd
+ *
+ * Sending a packet from HOST1 to HOST2, the packet is mangled as follows (NAT'ed to HOST2):
+ * IPa/MACa---------->IPd/MACb:::::IPc/MACc---------->IPd/MACd
+ *
+ * Reply:
+ * IPa/MACa<----------IPd/MACb:::::IPc/MACc<----------IPd/MACd
+ *
+ * NOTE: IPx is considered to be IP addressing, protocol and port information combined.
+ */
+
+/*
+ * nss_connmgr_ipv4_mac_addr_get()
+ *	Return the hardware (MAC) address of the given IPv4 address, if any.
+ *
+ * Returns 0 on success or a negative result on failure.
+ * We look up the rtable entry for the address and,
+ * from its neighbour structure,obtain the hardware address.
+ * This means we will also work if the neighbours are routers too.
+ */
+static int nss_connmgr_ipv4_mac_addr_get(ipv4_addr_t addr, mac_addr_t mac_addr)
+{
+	struct neighbour *neigh;
+	struct rtable *rt;
+	struct dst_entry *dst;
+
+	/*
+	 * Get the MAC addresses that correspond to source and destination host addresses.
+	 * We look up the rtable entries and, from its neighbour structure, obtain the hardware address.
+	 * This means we will also work if the neighbours are routers too.
+	 */
+	rt = ip_route_output(&init_net, addr, 0, 0, 0);
+	if (IS_ERR(rt)) {
+		return -1;
+	}
+
+	dst = (struct dst_entry *)rt;
+
+	rcu_read_lock();
+	neigh = dst_get_neighbour_noref(dst);
+	if (!neigh) {
+		rcu_read_unlock();
+		dst_release(dst);
+		return -2;
+	}
+	if (!(neigh->nud_state & NUD_VALID)) {
+		rcu_read_unlock();
+		dst_release(dst);
+		return -3;
+	}
+	if (!neigh->dev) {
+		rcu_read_unlock();
+		dst_release(dst);
+		return -4;
+	}
+	memcpy(mac_addr, neigh->ha, (size_t)neigh->dev->addr_len);
+	rcu_read_unlock();
+
+	dst_release(dst);
+
+	/*
+	 * If this mac looks like a multicast then it MAY be either truly multicast or it could be broadcast
+	 * Either way we fail!  We don't want to deal with multicasts or any sort because the NSS cannot deal with them.
+	 */
+	if (is_multicast_ether_addr(mac_addr)) {
+		NSS_CONNMGR_DEBUG_TRACE("Mac is multicast / broadcast - ignoring\n");
+		return -5;
+	}
+
+	return 0;
+}
+
+/*
+ * nss_connmgr_ipv4_post_routing_hook()
+ *	Called for packets about to leave the box - either locally generated or forwarded from another interface
+ */
+static unsigned int nss_connmgr_ipv4_post_routing_hook(unsigned int hooknum,
+				struct sk_buff *skb,
+				const struct net_device *in_unused,
+				const struct net_device *out,
+				int (*okfn)(struct sk_buff *))
+{
+	struct nss_ipv4_create unic;
+
+	struct net_device *in;
+	struct nf_conn *ct;
+	enum ip_conntrack_info ctinfo;
+	struct net_device *src_dev;
+	struct net_device *dest_dev;
+	struct nf_conntrack_tuple orig_tuple;
+	struct nf_conntrack_tuple reply_tuple;
+
+	nss_tx_status_t nss_tx_status;
+
+	/*
+	 * Don't process broadcast or multicast
+	 */
+	if (skb->pkt_type == PACKET_BROADCAST) {
+		NSS_CONNMGR_DEBUG_TRACE("Broadcast, ignoring: %p\n", skb);
+		return NF_ACCEPT;
+	}
+	if (skb->pkt_type == PACKET_MULTICAST) {
+		NSS_CONNMGR_DEBUG_TRACE("Multicast, ignoring: %p\n", skb);
+		return NF_ACCEPT;
+	}
+
+	/*
+	 * Only process packets being forwarded across the router - obtain the input interface for the skb
+	 * IMPORTANT 'in' must be released with dev_put(), this is not true for 'out'
+	 */
+	in = dev_get_by_index(&init_net, skb->skb_iif);
+	if  (!in) {
+		NSS_CONNMGR_DEBUG_TRACE("Not forwarded, ignoring: skb %p iif %d\n", skb, skb->skb_iif);
+		return NF_ACCEPT;
+	}
+
+	/*
+	 * Only work with standard 802.3 mac address sizes
+	 */
+	if (in->addr_len != 6) {
+		dev_put(in);
+		NSS_CONNMGR_DEBUG_TRACE("in device (%s) not 802.3 hw addr len (%u), ignoring: %p\n", in->name, (unsigned)in->addr_len, skb);
+		return NF_ACCEPT;
+	}
+	if (out->addr_len != 6) {
+		dev_put(in);
+		NSS_CONNMGR_DEBUG_TRACE("out device (%s) not 802.3 hw addr len (%u), ignoring: %p\n", out->name, (unsigned)out->addr_len, skb);
+		return NF_ACCEPT;
+	}
+
+	/*
+	 * Only process packets that are also tracked by conntrack
+	 */
+	ct = nf_ct_get(skb, &ctinfo);
+	if (!ct) {
+		dev_put(in);
+		NSS_CONNMGR_DEBUG_TRACE("No conntrack connection, ignoring: %p\n", skb);
+		return NF_ACCEPT;
+	}
+	NSS_CONNMGR_DEBUG_TRACE("skb: %p tracked by connection: %p\n", skb, ct);
+
+	/*
+	 * Special untracked connection is not monitored
+	 */
+	if (ct == &nf_conntrack_untracked) {
+		dev_put(in);
+		NSS_CONNMGR_DEBUG_TRACE("%p: Untracked connection\n", ct);
+		return NF_ACCEPT;
+	}
+
+	/*
+	 * Any connection needing support from a 'helper' (aka NAT ALG) is kept away from the Network Accelerator
+	 */
+	if (nfct_help(ct)) {
+		dev_put(in);
+		NSS_CONNMGR_DEBUG_TRACE("%p: Connection has helper\n", ct);
+		return NF_ACCEPT;
+	}
+
+	/*
+	 * Now examine conntrack to identify the protocol, IP addresses and portal information involved
+	 * IMPORTANT: The information here will be as the 'ORIGINAL' direction, i.e. who established the connection.
+	 * This MAY NOT be the same as the current packet direction, for example, this packet direction may be eth1->eth0 but
+	 * originally the connection may be been started from a packet going from eth0->eth1.
+	 */
+	orig_tuple = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple;
+	reply_tuple = ct->tuplehash[IP_CT_DIR_REPLY].tuple;
+	unic.protocol = (int32_t)orig_tuple.dst.protonum;
+
+	/*
+	 * Get addressing information, non-NAT first
+	 */
+	unic.src_ip = (ipv4_addr_t)orig_tuple.src.u3.ip;
+	unic.dest_ip = (ipv4_addr_t)orig_tuple.dst.u3.ip;
+
+	/*
+	 * NAT'ed addresses - note these are as seen from the 'reply' direction
+	 * When NAT does not apply to this connection these will be identical to the above.
+	 */
+	unic.src_ip_xlate = (ipv4_addr_t)reply_tuple.dst.u3.ip;
+	unic.dest_ip_xlate = (ipv4_addr_t)reply_tuple.src.u3.ip;
+
+	unic.flags = 0;
+
+	unic.return_pppoe_session_id = 0;
+	unic.flow_pppoe_session_id = 0;
+
+	switch (unic.protocol) {
+	case IPPROTO_TCP:
+		unic.src_port = (int32_t)orig_tuple.src.u.tcp.port;
+		unic.dest_port = (int32_t)orig_tuple.dst.u.tcp.port;
+		unic.src_port_xlate = (int32_t)reply_tuple.dst.u.tcp.port;
+		unic.dest_port_xlate = (int32_t)reply_tuple.src.u.tcp.port;
+		unic.flow_window_scale = ct->proto.tcp.seen[0].td_scale;
+		unic.flow_max_window = ct->proto.tcp.seen[0].td_maxwin;
+		unic.flow_end = ct->proto.tcp.seen[0].td_end;
+		unic.flow_max_end = ct->proto.tcp.seen[0].td_maxend;
+		unic.return_window_scale = ct->proto.tcp.seen[1].td_scale;
+		unic.return_max_window = ct->proto.tcp.seen[1].td_maxwin;
+		unic.return_end = ct->proto.tcp.seen[1].td_end;
+		unic.return_max_end = ct->proto.tcp.seen[1].td_maxend;
+		if ( nf_ct_tcp_be_liberal || nf_ct_tcp_no_window_check ||
+			(ct->proto.tcp.seen[0].flags & IP_CT_TCP_FLAG_BE_LIBERAL) ||
+			(ct->proto.tcp.seen[1].flags & IP_CT_TCP_FLAG_BE_LIBERAL)) {
+
+			unic.flags |= NSS_IPV4_CREATE_FLAG_NO_SEQ_CHECK;
+		}
+
+		/*
+		 * Don't try to manage a non-established connection.
+		 */
+		if (!test_bit(IPS_ASSURED_BIT, &ct->status)) {
+			dev_put(in);
+			NSS_CONNMGR_DEBUG_TRACE("%p: Non-established connection\n", ct);
+			return NF_ACCEPT;
+		}
+
+		/*
+		 * If the connection is shutting down do not manage it.
+		 * state can not be SYN_SENT, SYN_RECV because connection is assured
+		 * Not managed states: FIN_WAIT, CLOSE_WAIT, LAST_ACK, TIME_WAIT, CLOSE.
+		 */
+		spin_lock_bh(&ct->lock);
+		if (ct->proto.tcp.state != TCP_CONNTRACK_ESTABLISHED) {
+			spin_unlock_bh(&ct->lock);
+			dev_put(in);
+			NSS_CONNMGR_DEBUG_TRACE("%p: Connection in termination state %#X\n", ct, ct->proto.tcp.state);
+			return NF_ACCEPT;
+		}
+		spin_unlock_bh(&ct->lock);
+
+		break;
+
+	case IPPROTO_UDP:
+		unic.src_port = (int32_t)orig_tuple.src.u.udp.port;
+		unic.dest_port = (int32_t)orig_tuple.dst.u.udp.port;
+		unic.src_port_xlate = (int32_t)reply_tuple.dst.u.udp.port;
+		unic.dest_port_xlate = (int32_t)reply_tuple.src.u.udp.port;
+		break;
+
+	default:
+		/*
+		 * Streamengine compatibility - database stores non-ported protocols with port numbers equal to negative protocol number
+		 * to indicate unused.
+		 */
+		dev_put(in);
+		NSS_CONNMGR_DEBUG_TRACE("%p: Unhandled protocol %d\n", ct, unic.protocol);
+		return NF_ACCEPT;
+	}
+
+	/*
+	 * Get MAC addresses
+	 * NOTE: We are dealing with the ORIGINAL direction here so 'in' and 'out' dev may need
+	 * to be swapped if this packet is a reply
+	 */
+	if (ctinfo < IP_CT_IS_REPLY) {
+		src_dev = in;
+		dest_dev = (struct net_device *)out;
+		NSS_CONNMGR_DEBUG_TRACE("%p: dir: Original\n", ct);
+	} else {
+		src_dev = (struct net_device *)out;
+		dest_dev = in;
+		NSS_CONNMGR_DEBUG_TRACE("%p: dir: Reply\n", ct);
+	}
+
+	/*
+	 * Get the MAC addresses that correspond to source and destination host addresses.
+	 */
+	if (nss_connmgr_ipv4_mac_addr_get(unic.src_ip, unic.src_mac)) {
+		dev_put(in);
+		NSS_CONNMGR_DEBUG_TRACE("%p: Failed to find MAC address for src IP: %pI4\n", ct, &unic.src_ip);
+		return NF_ACCEPT;
+	}
+
+	if (nss_connmgr_ipv4_mac_addr_get(unic.src_ip_xlate, unic.src_mac_xlate)) {
+		dev_put(in);
+		NSS_CONNMGR_DEBUG_TRACE("%p: Failed to find MAC address for xlate src IP: %pI4\n", ct, &unic.src_ip_xlate);
+		return NF_ACCEPT;
+	}
+
+	/*
+	 * Do dest now
+	 */
+	if (nss_connmgr_ipv4_mac_addr_get(unic.dest_ip, unic.dest_mac)) {
+		dev_put(in);
+		NSS_CONNMGR_DEBUG_TRACE("%p: Failed to find MAC address for dest IP: %pI4\n", ct, &unic.dest_ip);
+		return NF_ACCEPT;
+	}
+
+	if (nss_connmgr_ipv4_mac_addr_get(unic.dest_ip_xlate, unic.dest_mac_xlate)) {
+		dev_put(in);
+		NSS_CONNMGR_DEBUG_TRACE("%p: Failed to find MAC address for xlate dest IP: %pI4\n", ct, &unic.dest_ip_xlate);
+		return NF_ACCEPT;
+	}
+
+	/*
+	 * Only devices that are NSS devices may be accelerated.
+	 */
+	unic.src_interface_num = nss_get_interface_number(nss_connmgr_ipv4.nss_context, src_dev);
+	if (unic.src_interface_num < 0) {
+		dev_put(in);
+		return NF_ACCEPT;
+	}
+
+	unic.dest_interface_num = nss_get_interface_number(nss_connmgr_ipv4.nss_context, dest_dev);
+	if (unic.dest_interface_num < 0) {
+		dev_put(in);
+		return NF_ACCEPT;
+	}
+
+	/*
+	 * Create the Network Accelerator connection cache entries
+	 *
+	 * NOTE: All of the information we have is from the point of view of who created the connection however
+	 * the skb may actually be in the 'reply' direction - which is important to know when configuring the NSS as we have to set the right information
+	 * on the right "match" and "forwarding" entries.
+	 * We can use the ctinfo to determine which direction the skb is in and then swap fields as necessary.
+	 */
+	unic.src_ip = ntohl(unic.src_ip);
+	unic.dest_ip = ntohl(unic.dest_ip);
+	unic.src_ip_xlate = ntohl(unic.src_ip_xlate);
+	unic.dest_ip_xlate = ntohl(unic.dest_ip_xlate);
+	unic.src_port = ntohs(unic.src_port);
+	unic.dest_port = ntohs(unic.dest_port);
+	unic.src_port_xlate = ntohs(unic.src_port_xlate);
+	unic.dest_port_xlate = ntohs(unic.dest_port_xlate);
+
+	/*
+	 * Get MTU values for source and destination interfaces.
+	 */
+	unic.from_mtu = in->mtu;
+	unic.to_mtu = out->mtu;
+
+	/*
+	 * We have everything we need (hopefully :-])
+	 */
+	NSS_CONNMGR_DEBUG_TRACE("\n%p: Conntrack connection\n"
+			"skb: %p\n"
+			"dir: %s\n"
+			"Protocol: %d\n"
+			"src_ip: " IPV4_ADDR_FMT ":%d\n"
+			"dest_ip: " IPV4_ADDR_FMT "%d"
+			"src_ip_xlate: " IPV4_ADDR_FMT "%d\n"
+			"dest_ip_xlate: " IPV4_ADDR_FMT "%d\n"
+			"src_mac: " MAC_FMT "\n"
+			"dest_mac: " MAC_FMT "\n"
+			"src_mac_xlate: " MAC_FMT "\n"
+			"dest_mac_xlate: " MAC_FMT "\n"
+			"src_dev: %s\n"
+			"dest_dev: %s\n"
+			"src_iface_num: %u\n"
+			"dest_iface_num: %u\n",
+			ct,
+			skb,
+			(ctinfo < IP_CT_IS_REPLY)? "Original" : "Reply",
+			unic.protocol,
+			IPV4_ADDR_TO_QUAD(unic.src_ip), unic.src_port,
+			IPV4_ADDR_TO_QUAD(unic.dest_ip), unic.dest_port,
+			IPV4_ADDR_TO_QUAD(unic.src_ip_xlate), unic.src_port_xlate,
+			IPV4_ADDR_TO_QUAD(unic.dest_ip_xlate), unic.dest_port_xlate,
+			MAC_AS_BYTES(unic.src_mac),
+			MAC_AS_BYTES(unic.dest_mac),
+			MAC_AS_BYTES(unic.src_mac_xlate),
+			MAC_AS_BYTES(unic.dest_mac_xlate),
+			src_dev->name,
+			dest_dev->name,
+			unic.src_interface_num,
+			unic.dest_interface_num);
+
+	/*
+	 * If operations have stopped then do not proceed further
+	 */
+	spin_lock_bh(&nss_connmgr_ipv4.lock);
+	if (unlikely(nss_connmgr_ipv4.stopped)) {
+		spin_unlock_bh(&nss_connmgr_ipv4.lock);
+		dev_put(in);
+		NSS_CONNMGR_DEBUG_TRACE("Stopped, ignoring: %p\n", skb);
+		return NF_ACCEPT;
+	}
+	spin_unlock_bh(&nss_connmgr_ipv4.lock);
+
+	nss_tx_status = nss_tx_create_ipv4_rule(nss_connmgr_ipv4.nss_context, &unic);
+
+	if (nss_tx_status == NSS_TX_SUCCESS) {
+		goto out;
+	} else if (nss_tx_status == NSS_TX_FAILURE_NOT_READY) {
+		NSS_CONNMGR_DEBUG_ERROR("NSS not ready to accept rule \n");
+
+		spin_lock_bh(&nss_connmgr_ipv4.lock);
+		nss_connmgr_ipv4.debug_stats[NSS_CONNMGR_IPV4_CREATE_FAIL]++;
+		spin_unlock_bh(&nss_connmgr_ipv4.lock);
+	} else {
+		NSS_CONNMGR_DEBUG_TRACE("NSS create rule failed  skb: %p\n", skb);
+
+		spin_lock_bh(&nss_connmgr_ipv4.lock);
+		nss_connmgr_ipv4.debug_stats[NSS_CONNMGR_IPV4_CREATE_FAIL]++;
+		spin_unlock_bh(&nss_connmgr_ipv4.lock);
+	}
+
+out:
+	/*
+	 * Release the interface on which this skb arrived
+	 */
+	dev_put(in);
+
+	return NF_ACCEPT;
+}
+
+/*
+ * nss_connmgr_ipv4_net_dev_callback()
+ *	Callback handler from the linux device driver for the Network Accelerator.
+ *
+ * The NSS passes sync and stats information to the device driver - which then passes them
+ * onto this callback. This arrangement means that dependencies on driver and conntrack modules
+ * are solely in this module. * These callbacks aim to keep conntrack connections alive and to ensure that connection
+ * state - especially for TCP connections that track sequence space for example - is kept in synchronisation
+ * before packets flow back through linux from a period of NSS processing.
+ */
+static void nss_connmgr_ipv4_net_dev_callback(struct nss_ipv4_cb_params *nicp)
+{
+	struct nf_conntrack_tuple_hash *h;
+	struct nf_conntrack_tuple tuple;
+	struct nf_conn *ct;
+	struct nf_conn_counter *acct;
+	struct nss_connmgr_ipv4_connection *connection;
+	struct neighbour *neigh;
+
+	struct nss_ipv4_sync *sync;
+	struct nss_ipv4_establish *establish;
+
+	struct net_device *flow_dev;
+	struct net_device *return_dev;
+
+	uint32_t arp_key;
+
+
+	switch (nicp->reason)
+	{
+		case NSS_IPV4_CB_REASON_ESTABLISH:
+			establish = &nicp->params.establish;
+
+			if (unlikely(establish->index >= NSS_CONNMGR_IPV4_CONN_MAX)) {
+				NSS_CONNMGR_DEBUG_WARN("Bad establish index: %d\n", establish->index);
+				return;
+			}
+
+			/*
+			 * Connection manager uses the same index value as NSS , for the connection table.
+			 * The index is sent by NSS as part of Establish (and sync) packets.
+			 */
+			connection = &nss_connmgr_ipv4.connection[establish->index];
+
+			NSS_CONNMGR_DEBUG_TRACE("NSS IPv4 Establish callback %d \n", establish->index);
+
+			spin_lock_bh(&nss_connmgr_ipv4.lock);
+
+			/*
+			 * TODO Capture the condition state == ESTABLISHED in a variable,
+			 * and use the variable to print the error message later to avoid spin unlock/lock
+			 */
+			if (unlikely(connection->state == NSS_CONNMGR_IPV4_STATE_ESTABLISHED)) {
+				spin_unlock_bh(&nss_connmgr_ipv4.lock);
+
+				NSS_CONNMGR_DEBUG_TRACE("Invalid establish callback. Conn already established : %d \n", establish->index);
+				spin_lock_bh(&nss_connmgr_ipv4.lock);
+
+			}
+
+			connection->state = NSS_CONNMGR_IPV4_STATE_ESTABLISHED;
+
+			connection->protocol = establish->protocol;
+			connection->src_interface = establish->flow_interface;
+			connection->src_addr = establish->flow_ip;
+			connection->src_port = establish->flow_ident;
+			connection->src_addr_xlate = establish->flow_ip_xlate;
+			connection->src_port_xlate = establish->flow_ident_xlate;
+
+			connection->dest_interface = establish->return_interface;
+			connection->dest_addr = establish->return_ip;
+			connection->dest_port = establish->return_ident;
+			connection->dest_addr_xlate = establish->return_ip_xlate;
+			connection->dest_port_xlate = establish->return_ident_xlate;
+
+			memset(connection->stats, 0, (8*NSS_CONNMGR_IPV4_STATS_MAX));
+
+			spin_unlock_bh(&nss_connmgr_ipv4.lock);
+
+			return;
+
+		case NSS_IPV4_CB_REASON_SYNC:
+			sync = &nicp->params.sync;
+
+			if (unlikely(sync->index >= NSS_CONNMGR_IPV4_CONN_MAX)) {
+				NSS_CONNMGR_DEBUG_WARN("Bad sync index: %d\n", sync->index);
+				return;
+			}
+
+			connection = &nss_connmgr_ipv4.connection[sync->index];
+
+			NSS_CONNMGR_DEBUG_TRACE("NSS IPv4 Sync callback %d %d \n", sync->index, sync->reason);
+
+			spin_lock_bh(&nss_connmgr_ipv4.lock);
+			/*
+			 * TODO Capture the condition state != ESTABLISHED in a variable,
+			 * and use the variable to print the error message later to avoid spin unlock/lock
+			 */
+			if (unlikely(connection->state != NSS_CONNMGR_IPV4_STATE_ESTABLISHED)) {
+				spin_unlock_bh(&nss_connmgr_ipv4.lock);
+				NSS_CONNMGR_DEBUG_TRACE("Invalid sync callback. Conn not established : %d \n", sync->index);
+				spin_lock_bh(&nss_connmgr_ipv4.lock);
+			}
+
+			spin_unlock_bh(&nss_connmgr_ipv4.lock);
+
+			switch (sync->reason) {
+				case NSS_IPV4_SYNC_REASON_STATS:
+					spin_lock_bh(&nss_connmgr_ipv4.lock);
+					connection->stats[NSS_CONNMGR_IPV4_ACCELERATED_RX_PKTS] += sync->flow_packet_count;
+					connection->stats[NSS_CONNMGR_IPV4_ACCELERATED_RX_BYTES] += sync->flow_byte_count;
+					connection->stats[NSS_CONNMGR_IPV4_ACCELERATED_TX_PKTS] += sync->return_packet_count;
+					connection->stats[NSS_CONNMGR_IPV4_ACCELERATED_TX_BYTES] += sync->return_byte_count;
+					spin_unlock_bh(&nss_connmgr_ipv4.lock);
+					break;
+
+				case NSS_IPV4_SYNC_REASON_FLUSH:
+				case NSS_IPV4_SYNC_REASON_EVICT:
+				case NSS_IPV4_SYNC_REASON_DESTROY:
+					spin_lock_bh(&nss_connmgr_ipv4.lock);
+					connection->state = NSS_CONNMGR_IPV4_STATE_INACTIVE;
+					spin_unlock_bh(&nss_connmgr_ipv4.lock);
+					break;
+
+				case NSS_IPV4_SYNC_REASON_PPPOE_DESTROY:
+					break;
+
+				default:
+					NSS_CONNMGR_DEBUG_TRACE("%d: Invalid Sync Message\n", sync->reason);
+					return;
+
+			}
+			break;
+
+		default:
+			NSS_CONNMGR_DEBUG_WARN("%d: Unhandled callback\n", nicp->reason);
+			return;
+	}
+
+	/*
+	 * Create a tuple so as to be able to look up a connection
+	 */
+	memset(&tuple, 0, sizeof(tuple));
+	tuple.src.u3.ip = ntohl(connection->src_addr);
+	tuple.src.u.all = ntohs((__be16)connection->src_port);
+	tuple.src.l3num = AF_INET;
+
+	tuple.dst.u3.ip = ntohl(connection->dest_addr);
+	tuple.dst.dir = IP_CT_DIR_ORIGINAL;
+	tuple.dst.protonum = (uint8_t)connection->protocol;
+	tuple.dst.u.all = ntohs((__be16)connection->dest_port);
+
+	NSS_CONNMGR_DEBUG_TRACE("\nNSS Update, lookup connection using\n"
+			"Protocol: %d\n"
+			"src_addr: %pI4:%d\n"
+			"dest_addr: %pI4:%d\n",
+			(int)tuple.dst.protonum,
+			&(tuple.src.u3.ip), (int)tuple.src.u.all,
+			&(tuple.dst.u3.ip), (int)tuple.dst.u.all);
+
+	/*
+	 * Look up conntrack connection
+	 */
+	h = nf_conntrack_find_get(&init_net, NF_CT_DEFAULT_ZONE, &tuple);
+	if (!h) {
+		NSS_CONNMGR_DEBUG_WARN("Conntrack not found for sync\n");
+		return;
+	}
+
+	ct = nf_ct_tuplehash_to_ctrack(h);
+	NF_CT_ASSERT(ct->timeout.data == (unsigned long)ct);
+
+	/*
+	 * Only update if this is not a fixed timeout
+	 */
+	if (!test_bit(IPS_FIXED_TIMEOUT_BIT, &ct->status)) {
+		ct->timeout.expires += sync->delta_jiffies;
+	}
+
+	acct = nf_conn_acct_find(ct);
+	if (acct) {
+		spin_lock_bh(&ct->lock);
+		atomic64_add(sync->flow_packet_count, &acct[IP_CT_DIR_ORIGINAL].packets);
+		atomic64_add(sync->flow_byte_count, &acct[IP_CT_DIR_ORIGINAL].bytes);
+
+		atomic64_add(sync->return_packet_count, &acct[IP_CT_DIR_REPLY].packets);
+		atomic64_add(sync->return_byte_count, &acct[IP_CT_DIR_REPLY].bytes);
+		spin_unlock_bh(&ct->lock);
+	}
+
+	switch (connection->protocol) {
+	case IPPROTO_TCP:
+		spin_lock_bh(&ct->lock);
+		if (ct->proto.tcp.seen[0].td_maxwin < sync->flow_max_window) {
+			ct->proto.tcp.seen[0].td_maxwin = sync->flow_max_window;
+		}
+		if ((int32_t)(ct->proto.tcp.seen[0].td_end - sync->flow_end) < 0) {
+			ct->proto.tcp.seen[0].td_end = sync->flow_end;
+		}
+		if ((int32_t)(ct->proto.tcp.seen[0].td_maxend - sync->flow_max_end) < 0) {
+			ct->proto.tcp.seen[0].td_maxend = sync->flow_max_end;
+		}
+		if (ct->proto.tcp.seen[1].td_maxwin < sync->return_max_window) {
+			ct->proto.tcp.seen[1].td_maxwin = sync->return_max_window;
+		}
+		if ((int32_t)(ct->proto.tcp.seen[1].td_end - sync->return_end) < 0) {
+			ct->proto.tcp.seen[1].td_end = sync->return_end;
+		}
+		if ((int32_t)(ct->proto.tcp.seen[1].td_maxend - sync->return_max_end) < 0) {
+			ct->proto.tcp.seen[1].td_maxend = sync->return_max_end;
+		}
+		spin_unlock_bh(&ct->lock);
+		break;
+	}
+
+	flow_dev = nss_get_interface_dev(nss_connmgr_ipv4.nss_context, connection->src_interface);
+	if (unlikely(!flow_dev)) {
+		NSS_CONNMGR_DEBUG_WARN("Invalid src dev reference from NSS \n");
+		goto out;
+	}
+
+	return_dev = nss_get_interface_dev(nss_connmgr_ipv4.nss_context, connection->dest_interface);
+	if (unlikely(!return_dev)) {
+		NSS_CONNMGR_DEBUG_WARN("Invalid dest dev reference from NSS \n");
+		goto out;
+	}
+
+	/*
+	 * Hold the net_device references
+	 */
+	dev_hold(flow_dev);
+	dev_hold(return_dev);
+
+	/*
+	 * Update ARP table.
+	 */
+	arp_key = ntohl(connection->src_addr);
+	neigh = neigh_lookup(&arp_tbl, &arp_key, flow_dev);
+	if (neigh) {
+		neigh_update(neigh, NULL, neigh->nud_state, NEIGH_UPDATE_F_WEAK_OVERRIDE);
+		neigh_release(neigh);
+	}
+
+	arp_key = ntohl(connection->dest_addr);
+	neigh = neigh_lookup(&arp_tbl, &arp_key, return_dev);
+	if (neigh) {
+		neigh_update(neigh, NULL, neigh->nud_state, NEIGH_UPDATE_F_WEAK_OVERRIDE);
+		neigh_release(neigh);
+	}
+
+	/*
+	 * Release the net_device references
+	 */
+	dev_put(flow_dev);
+	dev_put(return_dev);
+
+out:
+	/*
+	 * Release connection
+	 */
+	nf_ct_put(ct);
+
+	return;
+}
+
+#ifdef CONFIG_NF_CONNTRACK_EVENTS
+/*
+ * nss_connmgr_ipv4_conntrack_event()
+ *	Callback event invoked when conntrack connection state changes, currently we handle destroy events to quickly release state
+ */
+#ifdef CONFIG_NF_CONNTRACK_CHAIN_EVENTS
+static int nss_connmgr_ipv4_conntrack_event(struct notifier_block *this, unsigned long events, void *ptr)
+#else
+static int nss_connmgr_ipv4_conntrack_event(unsigned int events, struct nf_ct_event *item)
+#endif
+{
+	struct nss_ipv4_destroy unid;
+#ifdef CONFIG_NF_CONNTRACK_CHAIN_EVENTS
+	struct nf_ct_event *item = (struct nf_ct_event *)ptr;
+#endif
+	struct nf_conn *ct = item->ct;
+	struct nf_conntrack_tuple orig_tuple;
+	struct nf_conntrack_tuple reply_tuple;
+
+	nss_tx_status_t nss_tx_status;
+
+	/*
+	 * Basic sanity checks on the event
+	 */
+	if (!ct) {
+		NSS_CONNMGR_DEBUG_WARN("No ct in conntrack event callback\n");
+		return NOTIFY_DONE;
+	}
+	if (ct == &nf_conntrack_untracked) {
+		NSS_CONNMGR_DEBUG_TRACE("%p: ignoring untracked connn", ct);
+		return NOTIFY_DONE;
+	}
+
+	/*
+	 * Only interested if this is IPv4
+	 */
+	if (nf_ct_l3num(ct) != AF_INET) {
+		return NOTIFY_DONE;
+	}
+
+	/*
+	 * Only interested in destroy events
+	 */
+	if (!(events & (1 << IPCT_DESTROY))) {
+		return NOTIFY_DONE;
+	}
+
+	/*
+	 * Now examine conntrack to identify the protocol, IP addresses and portal information involved
+	 * IMPORTANT: The information here will be as the 'ORIGINAL' direction, i.e. who established the connection.
+	 * This MAY NOT be the same as the current packet direction, for example, this packet direction may be eth1->eth0 but
+	 * originally the connection may be been started from a packet going from eth0->eth1.
+	 */
+	orig_tuple = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple;
+	reply_tuple = ct->tuplehash[IP_CT_DIR_REPLY].tuple;
+	unid.protocol = (int32_t)orig_tuple.dst.protonum;
+
+	/*
+	 * Get addressing information, non-NAT first
+	 */
+	unid.src_ip = (ipv4_addr_t)orig_tuple.src.u3.ip;
+	unid.dest_ip = (ipv4_addr_t)orig_tuple.dst.u3.ip;
+
+	switch (unid.protocol) {
+	case IPPROTO_TCP:
+		unid.src_port = (int32_t)orig_tuple.src.u.tcp.port;
+		unid.dest_port = (int32_t)orig_tuple.dst.u.tcp.port;
+		break;
+
+	case IPPROTO_UDP:
+		unid.src_port = (int32_t)orig_tuple.src.u.udp.port;
+		unid.dest_port = (int32_t)orig_tuple.dst.u.udp.port;
+		break;
+
+	default:
+		/*
+		 * Streamengine compatibility - database stores non-ported protocols with port numbers equal to negative protocol number
+		 * to indicate unused.
+		 */
+		NSS_CONNMGR_DEBUG_TRACE("%p: Unhandled protocol %d\n", ct, unid.protocol);
+		unid.src_port = -unid.protocol;
+		unid.dest_port = -unid.protocol;
+	}
+
+	/*
+	 * Only deal with TCP or UDP
+	 */
+	if ((unid.protocol != IPPROTO_TCP) && (unid.protocol != IPPROTO_UDP)) {
+		return NOTIFY_DONE;
+	}
+
+	unid.src_ip = ntohl(unid.src_ip);
+	unid.dest_ip = ntohl(unid.dest_ip);
+	unid.src_port = ntohs(unid.src_port);
+	unid.dest_port = ntohs(unid.dest_port);
+
+	if (IS_LOCAL_HOST(unid.src_ip)) {
+		NSS_CONNMGR_DEBUG_TRACE("localhost packet ignored \n");
+		return NOTIFY_DONE;
+	}
+
+	NSS_CONNMGR_DEBUG_TRACE("\n%p: Connection destroyed\n"
+			"Protocol: %d\n"
+			"src_ip: %pI4:%d\n"
+			"dest_ip: %pI4:%d\n",
+			ct,
+			unid.protocol,
+			&(unid.src_ip), unid.src_port,
+			&(unid.dest_ip), unid.dest_port);
+
+	/*
+	 * Destroy the Network Accelerator connection cache entries.
+	 */
+	nss_tx_status = nss_tx_destroy_ipv4_rule(nss_connmgr_ipv4.nss_context, &unid);
+
+	if (nss_tx_status == NSS_TX_SUCCESS) {
+		goto out;
+	} else if (nss_tx_status == NSS_TX_FAILURE_NOT_READY) {
+		spin_lock_bh(&nss_connmgr_ipv4.lock);
+		nss_connmgr_ipv4.debug_stats[NSS_CONNMGR_IPV4_DESTROY_FAIL]++;
+		spin_unlock_bh(&nss_connmgr_ipv4.lock);
+		NSS_CONNMGR_DEBUG_ERROR("NSS not ready to accept 'destroy IPv4' rule \n");
+	} else {
+		spin_lock_bh(&nss_connmgr_ipv4.lock);
+		nss_connmgr_ipv4.debug_stats[NSS_CONNMGR_IPV4_DESTROY_FAIL]++;
+		spin_unlock_bh(&nss_connmgr_ipv4.lock);
+		NSS_CONNMGR_DEBUG_TRACE("NSS destroy rule fail \n");
+	}
+out:
+	return NOTIFY_DONE;
+}
+#endif
+
+/*
+ * nss_connmgr_ipv4_init_connection_table()
+ *	initialize connection table
+ */
+static void nss_connmgr_ipv4_init_connection_table(void)
+{
+	int32_t i,j;
+
+	spin_lock_bh(&nss_connmgr_ipv4.lock);
+	for (i = 0; (i < NSS_CONNMGR_IPV4_CONN_MAX); i++) {
+		nss_connmgr_ipv4.connection[i].state = NSS_CONNMGR_IPV4_STATE_INACTIVE;
+
+		for (j = 0; (j < NSS_CONNMGR_IPV4_STATS_MAX); j++) {
+			nss_connmgr_ipv4.connection[i].stats[j] = 0;
+		}
+	}
+	spin_unlock_bh(&nss_connmgr_ipv4.lock);
+
+	return;
+}
+
+
+/*
+ * nss_connmgr_ipv4_get_terminate()
+ */
+static ssize_t nss_connmgr_ipv4_get_terminate(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	ssize_t count;
+	int num;
+
+	/*
+	 * Operate under our locks
+	 */
+	spin_lock_bh(&nss_connmgr_ipv4.lock);
+	num = nss_connmgr_ipv4.terminate;
+	spin_unlock_bh(&nss_connmgr_ipv4.lock);
+
+	count = snprintf(buf, (ssize_t)PAGE_SIZE, "%d\n", num);
+	return count;
+}
+
+/*
+ * nss_connmgr_ipv4_set_terminate()
+ *	Writing anything to this 'file' will cause the default classifier to terminate
+ */
+static ssize_t nss_connmgr_ipv4_set_terminate(struct device *dev,
+				struct device_attribute *attr,
+				const char *buf, size_t count)
+{
+
+
+	NSS_CONNMGR_DEBUG_INFO("Terminate\n");
+	spin_lock_bh(&nss_connmgr_ipv4.lock);
+	nss_connmgr_ipv4.terminate = 1;
+	wake_up_process(nss_connmgr_ipv4.thread);
+	spin_unlock_bh(&nss_connmgr_ipv4.lock);
+
+	return count;
+}
+
+/*
+ * nss_connmgr_ipv4_get_stop()
+ * 	Get the value of "stopped" operational control variable
+ */
+static ssize_t nss_connmgr_ipv4_get_stop(struct device *dev,
+				struct device_attribute *attr,
+				char *buf)
+{
+	ssize_t count;
+	int num;
+
+	/*
+	 * Operate under our locks
+	 */
+	spin_lock_bh(&nss_connmgr_ipv4.lock);
+	num = nss_connmgr_ipv4.stopped;
+	spin_unlock_bh(&nss_connmgr_ipv4.lock);
+
+	count = snprintf(buf, (ssize_t)PAGE_SIZE, "%d\n", num);
+	return count;
+}
+
+/*
+ * nss_connmgr_ipv4_set_stop()
+ * 	Set the value of "stopped" operational control variable.
+ * 	This will stop further processing of packets and adding new rules
+ * 	to NSS (fastpath).
+ */
+static ssize_t nss_connmgr_ipv4_set_stop(struct device *dev,
+				struct device_attribute *attr,
+				const char *buf, size_t count)
+{
+	char num_buf[12];
+	int num;
+
+
+	/*
+	 * Get the number from buf into a properly z-termed number buffer
+	 */
+	if (count > 11) {
+		return 0;
+	}
+	memcpy(num_buf, buf, count);
+	num_buf[count] = '\0';
+	sscanf(num_buf, "%d", &num);
+	NSS_CONNMGR_DEBUG_TRACE("nss_connmgr_ipv4_stop = %d\n", num);
+
+	/*
+	 * Operate under our locks and stop further processing of packets
+	 */
+	spin_lock_bh(&nss_connmgr_ipv4.lock);
+	nss_connmgr_ipv4.stopped = num;
+	spin_unlock_bh(&nss_connmgr_ipv4.lock);
+
+	return count;
+}
+
+/*
+ * SysFS attributes for the default classifier itself.
+ */
+static const struct device_attribute nss_connmgr_ipv4_terminate_attr =
+		__ATTR(terminate, S_IWUGO | S_IRUGO, nss_connmgr_ipv4_get_terminate, nss_connmgr_ipv4_set_terminate);
+static const struct device_attribute nss_connmgr_ipv4_stop_attr =
+		__ATTR(stop, S_IWUGO | S_IRUGO, nss_connmgr_ipv4_get_stop, nss_connmgr_ipv4_set_stop);
+
+/*
+ * nss_connmgr_ipv4_thread_fn()
+ *	A thread to handle tasks that can only be done in thread context.
+ */
+static int nss_connmgr_ipv4_thread_fn(void *arg)
+{
+	int result = 0;
+
+	NSS_CONNMGR_DEBUG_INFO("NSS Connection manager thread start\n");
+
+	/*
+	 * Get reference to this module - we release it when the thread exits
+	 */
+	try_module_get(THIS_MODULE);
+
+	/*
+	 * Create /sys/nom_v4
+	 */
+	nss_connmgr_ipv4.nom_v4 = kobject_create_and_add("nom_v4", NULL);
+	if (unlikely(nss_connmgr_ipv4.nom_v4 == NULL)) {
+		NSS_CONNMGR_DEBUG_ERROR("Failed to register nom_v4\n");
+		goto task_cleanup_1;
+	}
+
+	/*
+	 * Create files, one for each parameter supported by this module
+	 */
+	result = sysfs_create_file(nss_connmgr_ipv4.nom_v4, &nss_connmgr_ipv4_terminate_attr.attr);
+	if (result) {
+		NSS_CONNMGR_DEBUG_ERROR("Failed to register terminate file %d\n", result);
+		goto task_cleanup_2;
+	}
+
+	/*
+	 * Register netfilter hooks - IPV4
+	 */
+	result = nf_register_hooks(nss_connmgr_ipv4_ops_post_routing, ARRAY_SIZE(nss_connmgr_ipv4_ops_post_routing));
+	if (result < 0) {
+		NSS_CONNMGR_DEBUG_ERROR("Can't register nf post routing hook %d\n", result);
+		goto task_cleanup_3;
+	}
+
+	result = sysfs_create_file(nss_connmgr_ipv4.nom_v4, &nss_connmgr_ipv4_stop_attr.attr);
+	if (result) {
+		NSS_CONNMGR_DEBUG_ERROR("Failed to register stop file %d\n", result);
+		goto task_cleanup_4;
+	}
+
+#ifdef CONFIG_NF_CONNTRACK_EVENTS
+	/*
+	 * Eventing subsystem is available so we register a notifier hook to get fast notifications of expired connections
+	 */
+	result = nf_conntrack_register_notifier(&init_net, &nss_connmgr_ipv4.conntrack_notifier);
+	if (result < 0) {
+		NSS_CONNMGR_DEBUG_ERROR("Can't register nf notifier hook %d\n", result);
+		goto task_cleanup_5;
+	}
+#endif
+
+	/*
+	 * Register this module with the Linux NSS driver (net_device)
+	 */
+	nss_connmgr_ipv4.nss_context = nss_register_ipv4_mgr(nss_connmgr_ipv4_net_dev_callback);
+
+	/*
+	 * Initialize connection table
+	 */
+	nss_connmgr_ipv4_init_connection_table();
+
+	/*
+	 * Allow wakeup signals
+	 */
+	allow_signal(SIGCONT);
+	spin_lock_bh(&nss_connmgr_ipv4.lock);
+	while (!nss_connmgr_ipv4.terminate) {
+		/*
+		 * Sleep and wait for an instruction
+		 */
+		spin_unlock_bh(&nss_connmgr_ipv4.lock);
+		NSS_CONNMGR_DEBUG_TRACE("nss_connmgr_ipv4 sleep\n");
+		set_current_state(TASK_INTERRUPTIBLE);
+		schedule();
+		spin_lock_bh(&nss_connmgr_ipv4.lock);
+	}
+	spin_unlock_bh(&nss_connmgr_ipv4.lock);
+	NSS_CONNMGR_DEBUG_INFO("nss_connmgr_ipv4 terminate\n");
+
+	result = 0;
+
+	nss_unregister_ipv4_mgr();
+
+#ifdef CONFIG_NF_CONNTRACK_EVENTS
+	nf_conntrack_unregister_notifier(&init_net, &nss_connmgr_ipv4.conntrack_notifier);
+task_cleanup_5:
+#endif
+	sysfs_remove_file(nss_connmgr_ipv4.nom_v4, &nss_connmgr_ipv4_stop_attr.attr);
+task_cleanup_4:
+	nf_unregister_hooks(nss_connmgr_ipv4_ops_post_routing, ARRAY_SIZE(nss_connmgr_ipv4_ops_post_routing));
+task_cleanup_3:
+	sysfs_remove_file(nss_connmgr_ipv4.nom_v4, &nss_connmgr_ipv4_terminate_attr.attr);
+task_cleanup_2:
+	kobject_put(nss_connmgr_ipv4.nom_v4);
+task_cleanup_1:
+
+	module_put(THIS_MODULE);
+	return result;
+}
+/*
+ * nss_connmgr_ipv4_read_conn_stats
+ *      Read IPV4 stats
+ */
+static ssize_t nss_connmgr_ipv4_read_conn_stats(struct file *fp, char __user *ubuf, size_t sz, loff_t *ppos)
+{
+	int32_t i;
+	int32_t j;
+	int32_t k;
+	size_t size_al = NSS_CONNMGR_IPV4_DEBUGFS_BUF_SZ;
+	size_t size_wr = 0;
+	ssize_t bytes_read = 0;
+
+	/*
+	 * Temporary array to hold statistics for printing. To avoid holding
+	 * spinlocks for long time while printing, we copy the stats to this
+	 * temporary buffer while holding the spinlock, unlock and then print.
+	 * Also to avoid using big chunk of memory on stack, we use an array of
+	 * only 8 connections, and print stats for 8 connections at a time
+	 */
+	uint64_t stats[8][NSS_CONNMGR_IPV4_STATS_MAX];
+	nss_connmgr_ipv4_conn_state_t  state[8];
+
+	char *lbuf = kzalloc(size_al, GFP_KERNEL);
+	if (unlikely(lbuf == NULL)) {
+		NSS_CONNMGR_DEBUG_WARN("Could not allocate memory for local statistics buffer \n");
+		return 0;
+	}
+
+	/*
+	 * TODO : Handle buffer full conditions by putting some checks , so it
+	 * doesn't crash or print something rubbish
+	 */
+	size_wr = scnprintf(lbuf, size_al,"connection mgr ipv4 stats :\n\n");
+
+	for (i = 0; (i < NSS_CONNMGR_IPV4_CONN_MAX) && (size_wr < size_al) ; i+=8) {
+
+		/* 1. Take a lock */
+		spin_lock_bh(&nss_connmgr_ipv4.lock);
+
+		/* 2. Copy stats for 8 connections into local buffer */
+		for (j = 0; j < 8; j++) {
+			state[j] = nss_connmgr_ipv4.connection[i+j].state;
+			for (k = 0; (k < NSS_CONNMGR_IPV4_STATS_MAX); k++) {
+				stats[j][k] = nss_connmgr_ipv4.connection[i+j].stats[k];
+			}
+		}
+
+		/* 3. Release the lock */
+		spin_unlock_bh(&nss_connmgr_ipv4.lock);
+
+		/* 4. Print stats for 8 connections */
+		for (j = 0; (j < 8); j++)
+		{
+			size_wr += scnprintf(lbuf + size_wr, size_al - size_wr,
+					"------------  Connection %d ( %s )---------------\n",
+					(i+j), nss_connmgr_ipv4_conn_state_str[state[j]]);
+
+			for (k = 0; (k < NSS_CONNMGR_IPV4_STATS_MAX); k++) {
+				size_wr += scnprintf(lbuf + size_wr, size_al - size_wr,
+						"%s = %llu\n", nss_connmgr_ipv4_conn_stats_str[k], stats[j][k]);
+			}
+		}
+	}
+
+	if (size_wr == size_al) {
+		NSS_CONNMGR_DEBUG_WARN("Print Buffer not available for debug stats \n");
+	}
+	else {
+		size_wr += scnprintf(lbuf + size_wr, size_al - size_wr,"\nconnection mgr stats end\n\n");
+	}
+
+	bytes_read = simple_read_from_buffer(ubuf, sz, ppos, lbuf, strlen(lbuf));
+	kfree(lbuf);
+
+	return bytes_read;
+}
+
+/*
+ * nss_connmgr_ipv4_read_debug_stats
+ *	Read connection manager debug stats
+ */
+static ssize_t nss_connmgr_ipv4_read_debug_stats(struct file *fp, char __user *ubuf, size_t sz, loff_t *ppos)
+{
+	int32_t i;
+
+	/*
+	 * max output lines = #stats + start tag line + end tag line + three blank lines
+	 */
+	uint32_t max_output_lines = NSS_CONNMGR_IPV4_DEBUG_STATS_MAX + 5;
+	size_t size_al = NSS_CONNMGR_IPV4_MAX_STR_LENGTH * max_output_lines;
+	size_t size_wr = 0;
+	ssize_t bytes_read = 0;
+	uint32_t stats_shadow[NSS_CONNMGR_IPV4_DEBUG_STATS_MAX];
+
+	char *lbuf = kzalloc(size_al, GFP_KERNEL);
+	if (unlikely(lbuf == NULL)) {
+		NSS_CONNMGR_DEBUG_WARN("Could not allocate memory for local statistics buffer");
+		return 0;
+	}
+
+	size_wr = scnprintf(lbuf, size_al,"debug stats start:\n\n");
+	spin_lock_bh(&nss_connmgr_ipv4.lock);
+
+	for (i = 0; (i < NSS_CONNMGR_IPV4_MAX_STR_LENGTH); i++) {
+		stats_shadow[i] = nss_connmgr_ipv4.debug_stats[i];
+	}
+
+	spin_unlock_bh(&nss_connmgr_ipv4.lock);
+
+	for (i = 0; (i < NSS_CONNMGR_IPV4_DEBUG_STATS_MAX); i++) {
+		size_wr += scnprintf(lbuf + size_wr, size_al - size_wr,
+					"%s = %u\n", nss_connmgr_ipv4_debug_stats_str[i], stats_shadow[i]);
+	}
+
+	size_wr += scnprintf(lbuf + size_wr, size_al - size_wr,"\ndebug stats end\n\n");
+	bytes_read = simple_read_from_buffer(ubuf, sz, ppos, lbuf, strlen(lbuf));
+	kfree(lbuf);
+
+	return bytes_read;
+}
+
+/*
+ * nss_connmgr_ipv4_clear_stats
+ *      Clear IPV4 stats
+ */
+static ssize_t nss_connmgr_ipv4_clear_stats(struct file *fp, const char __user *ubuf, size_t count , loff_t *ppos)
+{
+	int32_t i,j;
+
+	spin_lock_bh(&nss_connmgr_ipv4.lock);
+	for (i = 0; (i < NSS_CONNMGR_IPV4_CONN_MAX); i++) {
+		for (j = 0; (j < NSS_CONNMGR_IPV4_STATS_MAX); j++) {
+			nss_connmgr_ipv4.connection[i].stats[j] = 0;
+		}
+	}
+	spin_unlock_bh(&nss_connmgr_ipv4.lock);
+
+	return count;
+}
+
+/*
+ * nss_connmgr_ipv4_module_get()
+ *	Take a reference to the module
+ */
+void nss_connmgr_ipv4_module_get(void)
+{
+	try_module_get(THIS_MODULE);
+}
+EXPORT_SYMBOL(nss_connmgr_ipv4_module_get);
+
+/*
+ * nss_connmgr_ipv4_module_put()
+ *	Release a reference to the module
+ */
+void nss_connmgr_ipv4_module_put(void)
+{
+	module_put(THIS_MODULE);
+}
+EXPORT_SYMBOL(nss_connmgr_ipv4_module_put);
+
+/*
+ * nss_connmgr_ipv4_init()
+ */
+static int __init nss_connmgr_ipv4_init(void)
+{
+	struct dentry *dent, *dfile;
+
+	NSS_CONNMGR_DEBUG_INFO("NSS Connection Manager Module init\n");
+
+	/*
+	 * Initialise our global database lock
+	 */
+	spin_lock_init(&nss_connmgr_ipv4.lock);
+
+	/*
+	 * Create a thread to handle the start/stop.
+	 * NOTE: We use a thread as some things we need to do cannot be done in this context
+	 */
+	nss_connmgr_ipv4.terminate = 0;
+	nss_connmgr_ipv4.thread = kthread_create(nss_connmgr_ipv4_thread_fn, NULL, "%s", "nss_connmgr_ipv4_thread");
+	if (!nss_connmgr_ipv4.thread) {
+		return -EINVAL;
+	}
+	wake_up_process(nss_connmgr_ipv4.thread);
+
+	/*
+	 * Create debugfs files for stats
+	 */
+	dent = debugfs_create_dir("qca-nss-connmgr-ipv4", NULL);
+
+	if (unlikely(dent == NULL)) {
+
+		/*
+		 * non-availability of debugfs dir is not catastrophe.
+		 * Print a message and gracefully return
+		 */
+		NSS_CONNMGR_DEBUG_WARN("Failed to create nss_connmgr_ipv4 dir in debugfs \n");
+		return 0;
+	}
+
+	nss_connmgr_ipv4.dent = dent;
+
+	dfile = debugfs_create_file("connection_stats", S_IRUGO , dent, &nss_connmgr_ipv4, &nss_connmgr_ipv4_show_stats_ops);
+
+	if (unlikely(dfile == NULL))
+	{
+		NSS_CONNMGR_DEBUG_WARN("Failed to create nss_connmgr_ipv4/connection_stats in debugfs \n");
+		debugfs_remove(dent);
+	}
+
+	dfile = debugfs_create_file("debug_stats", S_IRUGO , dent, &nss_connmgr_ipv4, &nss_connmgr_ipv4_show_debug_stats_ops);
+
+	if (unlikely(dfile == NULL))
+	{
+		NSS_CONNMGR_DEBUG_WARN("Failed to create nss_connmgr_ipv4/debug_stats in debugfs \n");
+		debugfs_remove(dent);
+	}
+
+	dfile = debugfs_create_file("clear_stats", S_IRUGO | S_IWUSR , dent, &nss_connmgr_ipv4, &nss_connmgr_ipv4_clear_stats_ops);
+
+	if (unlikely(dfile == NULL))
+	{
+		NSS_CONNMGR_DEBUG_WARN("Failed to create nss_connmgr_ipv4/clear_stats in debugfs \n");
+		debugfs_remove(dent);
+	}
+
+
+	return 0;
+}
+
+/*
+ * nss_connmgr_ipv4_exit()
+ * 	Module exit function
+ */
+static void __exit nss_connmgr_ipv4_exit(void)
+{
+	/*
+	 * Remove debugfs tree
+	 */
+	if (likely(nss_connmgr_ipv4.dent != NULL)) {
+		debugfs_remove_recursive(nss_connmgr_ipv4.dent);
+	}
+
+	NSS_CONNMGR_DEBUG_INFO("NSS Connection Manager Module exit\n");
+}
+
+module_init(nss_connmgr_ipv4_init);
+module_exit(nss_connmgr_ipv4_exit);
+
+MODULE_AUTHOR("Qualcomm Atheros Inc");
+MODULE_DESCRIPTION("NSS IPv4 Connection manager");
+MODULE_LICENSE("Dual BSD/GPL");
+
diff --git a/nss_connmgr_ipv6.c b/nss_connmgr_ipv6.c
new file mode 100755
index 0000000..48be92f
--- /dev/null
+++ b/nss_connmgr_ipv6.c
@@ -0,0 +1,1629 @@
+/* * Copyright (c) 2013 Qualcomm Atheros, Inc. * */
+
+/*
+ * @file
+ * This file is the NSS connection manager for managing IPv6 connections.It
+ * forms an interface between the fast-path NSS driver and Linux
+ * connection track module for updating/syncing connection level information
+ * between the two.It is responsible for maintaing  all connection (flow) level
+ * information and statistics for all fast path connections.
+ *
+ * ------------------------REVISION HISTORY-----------------------------
+ * Qualcomm Atheros         01/Mar/2013              Created
+ */
+
+#include <linux/types.h>
+#include <linux/ip.h>
+#include <linux/tcp.h>
+#include <linux/module.h>
+#include <linux/skbuff.h>
+#include <linux/icmp.h>
+#include <linux/sysctl.h>
+#include <linux/kthread.h>
+#include <linux/sysdev.h>
+#include <linux/fs.h>
+#include <linux/pkt_sched.h>
+#include <linux/string.h>
+#include <linux/debugfs.h>
+#include <linux/netdevice.h>
+#include <net/ip.h>
+#include <net/ipv6.h>
+#include <net/ip6_fib.h>
+#include <net/ip6_route.h>
+#include <net/tcp.h>
+#include <asm/unaligned.h>
+#include <asm/uaccess.h>	/* for put_user */
+
+#include <linux/netfilter_ipv6.h>
+#include <linux/netfilter_bridge.h>
+#include <linux/if_bridge.h>
+#include <net/netfilter/nf_conntrack.h>
+#include <net/netfilter/nf_conntrack_acct.h>
+#include <net/netfilter/nf_conntrack_helper.h>
+#include <net/netfilter/nf_conntrack_l4proto.h>
+#include <net/netfilter/nf_conntrack_l3proto.h>
+#include <net/netfilter/nf_conntrack_zones.h>
+#include <net/netfilter/nf_conntrack_core.h>
+#include <net/netfilter/ipv6/nf_conntrack_ipv6.h>
+#include <net/netfilter/ipv6/nf_defrag_ipv6.h>
+
+#include <net/arp.h>
+
+#include "nss_api_if.h"
+
+/*
+ * Debug output levels
+ * 0 = OFF
+ * 1 = ASSERTS / ERRORS
+ * 2 = 1 + WARN
+ * 3 = 2 + INFO
+ * 4 = 3 + TRACE
+ */
+#if (NSS_CONNMGR_DEBUG_LEVEL < 1)
+#define NSS_CONNMGR_DEBUG_ASSERT(s, ...)
+#define NSS_CONNMGR_DEBUG_ERROR(s, ...)
+#define NSS_CONNMGR_DEBUG_CHECK_MAGIC(i, m, s, ...)
+#define NSS_CONNMGR_DEBUG_SET_MAGIC(i, m)
+#define NSS_CONNMGR_DEBUG_CLEAR_MAGIC(i)
+#else
+#define NSS_CONNMGR_DEBUG_ASSERT(c, s, ...) if (!(c)) { printk("%s:%d:ASSERT:", __FILE__, __LINE__);printk(s, ##__VA_ARGS__); while(1); }
+#define NSS_CONNMGR_DEBUG_ERROR(s, ...) printk("%s:%d:ERROR:", __FILE__, __LINE__);printk(s, ##__VA_ARGS__)
+#define NSS_CONNMGR_DEBUG_CHECK_MAGIC(i, m, s, ...) if (i->magic != m) { NSS_CONNMGR_DEBUG_ASSERT(FALSE, s, ##__VA_ARGS__); }
+#define NSS_CONNMGR_DEBUG_SET_MAGIC(i, m) i->magic = m
+#define NSS_CONNMGR_DEBUG_CLEAR_MAGIC(i) i->magic = 0
+#endif
+
+#if (NSS_CONNMGR_DEBUG_LEVEL < 2)
+#define NSS_CONNMGR_DEBUG_WARN(s, ...)
+#else
+#define NSS_CONNMGR_DEBUG_WARN(s, ...) { printk("%s:%d:WARN:", __FILE__, __LINE__);printk(s, ##__VA_ARGS__); }
+#endif
+
+#if (NSS_CONNMGR_DEBUG_LEVEL < 3)
+#define NSS_CONNMGR_DEBUG_INFO(s, ...)
+#else
+#define NSS_CONNMGR_DEBUG_INFO(s, ...) { printk("%s:%d:INFO:", __FILE__, __LINE__);printk(s, ##__VA_ARGS__); }
+#endif
+
+#if (NSS_CONNMGR_DEBUG_LEVEL < 4)
+#define NSS_CONNMGR_DEBUG_TRACE(s, ...)
+#else
+#define NSS_CONNMGR_DEBUG_TRACE(s, ...) { printk("%s:%d:TRACE:", __FILE__, __LINE__);printk(s, ##__VA_ARGS__); }
+#endif
+
+
+/**
+ * This macro converts format for IPv6 address (from Linux to NSS)
+ */
+#define IN6_ADDR_TO_IPV6_ADDR(ipv6, in6) \
+	{ \
+		((uint32_t *)ipv6)[0] = in6.in6_u.u6_addr32[0]; \
+		((uint32_t *)ipv6)[1] = in6.in6_u.u6_addr32[1]; \
+		((uint32_t *)ipv6)[2] = in6.in6_u.u6_addr32[2]; \
+		((uint32_t *)ipv6)[3] = in6.in6_u.u6_addr32[3]; \
+	}
+
+/**
+ * This macro converts format for IPv6 address (from NSS to Linux)
+ */
+#define IPV6_ADDR_TO_IN6_ADDR(in6, ipv6) \
+	{ \
+		in6.in6_u.u6_addr32[0] = ((uint32_t *)ipv6)[0]; \
+		in6.in6_u.u6_addr32[1] = ((uint32_t *)ipv6)[1]; \
+		in6.in6_u.u6_addr32[2] = ((uint32_t *)ipv6)[2]; \
+		in6.in6_u.u6_addr32[3] = ((uint32_t *)ipv6)[3]; \
+	}
+
+/**
+ * This macro converts format for IPv6 address (from NSS to Linux)
+ */
+#define IPV6_ADDR_NTOH_IN6_ADDR(in6, ipv6) \
+	{ \
+		in6.in6_u.u6_addr32[0] = ntohl(((uint32_t *)ipv6)[0]); \
+		in6.in6_u.u6_addr32[1] = ntohl(((uint32_t *)ipv6)[1]); \
+		in6.in6_u.u6_addr32[2] = ntohl(((uint32_t *)ipv6)[2]); \
+		in6.in6_u.u6_addr32[3] = ntohl(((uint32_t *)ipv6)[3]); \
+	}
+/**
+ * This macro converts IPv6 address from network order to host order
+ */
+#define IPV6_ADDR_NTOH(ipv6_h, ipv6_n) \
+	{ \
+		ipv6_h[0] = (ntohl(ipv6_n[0])); \
+		ipv6_h[1] = (ntohl(ipv6_n[1])); \
+		ipv6_h[2] = (ntohl(ipv6_n[2])); \
+		ipv6_h[3] = (ntohl(ipv6_n[3])); \
+	}
+
+
+/*
+ * Custom types recognised within the Connection Manager (Linux independent)
+ */
+typedef uint8_t mac_addr_t[6];
+typedef uint32_t ipv6_addr_t[4];
+
+/*
+ * Displaying addresses
+ */
+#define MAC_AS_BYTES(mac_addr) mac_addr[5], mac_addr[4], mac_addr[3],mac_addr[2], mac_addr[1], mac_addr[0]
+
+#define IPV6_ADDR_OCTAL_FMT "%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x"
+
+#define IPV6_ADDR_TO_OCTAL(ipv6) ((uint16_t *)ipv6)[0], ((uint16_t *)ipv6)[1], ((uint16_t *)ipv6)[2], ((uint16_t *)ipv6)[3], ((uint16_t *)ipv6)[4], ((uint16_t *)ipv6)[5], ((uint16_t *)ipv6)[6], ((uint16_t *)ipv6)[7]
+
+#define MAC_FMT "%02x:%02x:%02x:%02x:%02x:%02x"
+
+/*
+ * Max NSS ipv6 Flow entries
+ */
+#define NSS_CONNMGR_IPV6_CONN_MAX 256
+
+/*
+ * size of buffer allocated for stats printing (using debugfs)
+ */
+#define NSS_CONNMGR_IPV6_DEBUGFS_BUF_SZ (NSS_CONNMGR_IPV6_CONN_MAX*512)
+
+/*
+ * Maximum string length:
+ * This should be equal to maximum string size of any stats
+ * inclusive of stats value
+ */
+#define NSS_CONNMGR_IPV6_MAX_STR_LENGTH 96
+enum nss_connmgr_ipv6_conn_stats {
+	NSS_CONNMGR_IPV6_ACCELERATED_RX_PKTS = 0,
+					/* Accelerated ipv6 RX packets */
+	NSS_CONNMGR_IPV6_ACCELERATED_RX_BYTES,
+					/* Accelerated ipv6 RX bytes */
+	NSS_CONNMGR_IPV6_ACCELERATED_TX_PKTS,
+					/* Accelerated ipv6 TX packets */
+	NSS_CONNMGR_IPV6_ACCELERATED_TX_BYTES,
+					/* Accelerated ipv6 TX bytes */
+	NSS_CONNMGR_IPV6_STATS_MAX
+};
+
+typedef enum nss_connmgr_ipv6_conn_statistics nss_connmgr_ipv6_conn_statistics_t;
+
+/*
+ * Debug statistics
+ */
+enum nss_connmgr_ipv6_debug_statistics {
+	NSS_CONNMGR_IPV6_CREATE_FAIL,
+				/* Rule create failures */
+	NSS_CONNMGR_IPV6_DESTROY_FAIL,
+				/* Rule destroy failures */
+	NSS_CONNMGR_IPV6_ESTABLISH_MISS,
+				/* No establish response from NSS  */
+	NSS_CONNMGR_IPV6_DESTROY_MISS,
+				/* No Flush/Evict/Destroy response from NSS */
+	NSS_CONNMGR_IPV6_DEBUG_STATS_MAX
+};
+
+typedef enum nss_connmgr_ipv6_debug_statistics nss_connmgr_debug_ipv6_statistics_t;
+
+/*
+ * nss_connmgr_ipv6_conn_stats_str
+ *      Connection statistics strings
+ */
+static char *nss_connmgr_ipv6_conn_stats_str[] = {
+	 "rx_pkts",
+	 "rx_bytes",
+	 "tx_pkts",
+	 "tx_bytes",
+};
+
+/*
+ * nss_connmgr_ipv6_stats_str
+ *      Debug statistics strings
+ */
+static char *nss_connmgr_ipv6_debug_stats_str[] = {
+	 "create_fail",
+	 "destroy_fail",
+	 "establish_miss",
+	 "destroy_miss",
+};
+
+/*
+ * Connection states as defined by connection manager
+ *
+ * 	INACTIVE	Connection is not active
+ * 	ESTABLISHED	Connection is established, and NSS sends periodic sync messages
+ * 	STALE		Linux and NSS are out of sync.
+ * 			Conntrack -> Conn Mgr sent a rule destroy command,
+ * 			but the command did  not reach NSS
+ *
+ */
+typedef enum  {
+	NSS_CONNMGR_IPV6_STATE_INACTIVE,
+	NSS_CONNMGR_IPV6_STATE_ESTABLISHED,
+	NSS_CONNMGR_IPV6_STATE_STALE,
+} nss_connmgr_ipv6_conn_state_t;
+
+/*
+ * Connection states strings
+ */
+static char *nss_connmgr_ipv6_conn_state_str[] = {
+	"inactive",
+	"established",
+	"stale",
+};
+
+/*
+ * ipv6 statistics
+ */
+struct nss_connmgr_ipv6_connection {
+	uint8_t  state;					/* Connection state - established, not established, destroyed */
+	uint8_t  protocol;				/* Protocol number */
+	int32_t  src_interface;				/* Flow interface number */
+	uint32_t src_addr[4];				/* Source address, i.e. the creator of the connection */
+	int32_t  src_port;				/* Source port */
+	int32_t  dest_interface;			/* Return interface number */
+	uint32_t dest_addr[4];				/* Destination address, i.e. the to whom the connection was created */
+	int32_t  dest_port;				/* Destination port */
+	uint64_t stats[NSS_CONNMGR_IPV6_STATS_MAX];	/* Connection statistics */
+	uint32_t last_sync;				/* Last sync time as jiffies */
+};
+
+/*
+ * Global connection manager instance object.
+ */
+struct nss_connmgr_ipv6_instance {
+	spinlock_t lock;	/* Lock to Protect against SMP access. */
+	struct kobject *nom_v6;
+				/* Sysfs link. Represents the sysfs folder /sys/nom_v6 */
+	int32_t stopped;
+				/* General operational control.When non-zero further traffic will not be processed */
+	int32_t terminate;
+				/* Signal to tell the control thread to terminate */
+	struct task_struct *thread;
+				/* Control thread */
+	void *nss_context;
+				/* Registration context used to identify the manager in calls to the NSS driver */
+#ifdef CONFIG_NF_CONNTRACK_CHAIN_EVENTS
+	struct notifier_block conntrack_notifier;
+				/* NF conntrack event system to monitor connection tracking changes */
+#else
+	struct nf_ct_event_notifier conntrack_notifier;
+				/* NF conntrack event system to monitor connection tracking changes */
+#endif
+	struct nss_connmgr_ipv6_connection connection[NSS_CONNMGR_IPV6_CONN_MAX];
+				/* Connection Table */
+	struct dentry *dent;	/* Debugfs directory */
+	uint32_t debug_stats[NSS_CONNMGR_IPV6_DEBUG_STATS_MAX];
+				/* Debug statistics */
+};
+
+static unsigned int nss_connmgr_ipv6_post_routing_hook(unsigned int hooknum,
+				struct sk_buff *skb,
+				const struct net_device *in_unused,
+				const struct net_device *out,
+				int (*okfn)(struct sk_buff *));
+
+static ssize_t nss_connmgr_ipv6_read_stats(struct file *fp, char __user *ubuf, size_t sz, loff_t *ppos);
+static ssize_t nss_connmgr_ipv6_read_debug_stats(struct file *fp, char __user *ubuf, size_t sz, loff_t *ppos);
+static ssize_t nss_connmgr_ipv6_clear_stats(struct file *fp, const char __user *ubuf, size_t count, loff_t *ppos);
+
+#ifdef CONFIG_NF_CONNTRACK_CHAIN_EVENTS
+static int nss_connmgr_ipv6_conntrack_event(struct notifier_block *this, unsigned long events, void *ptr);
+#else
+static int nss_connmgr_ipv6_conntrack_event(unsigned int events, struct nf_ct_event *item);
+#endif
+
+
+static struct nss_connmgr_ipv6_instance nss_connmgr_ipv6 = {
+		.stopped = 0,
+		.terminate = 0,
+		.thread = NULL,
+		.nss_context = NULL,
+#ifdef CONFIG_NF_CONNTRACK_CHAIN_EVENTS
+		.conntrack_notifier = {
+			.notifier_call =nss_connmgr_ipv6_conntrack_event,
+		},
+#else
+		.conntrack_notifier = {
+			.fcn = nss_connmgr_ipv6_conntrack_event,
+		},
+#endif
+};
+
+/*
+ * nss_connmgr_ipv6_ops_post_routing[]
+ *
+ * Hooks into the post routing netfilter point -
+ * this will pick up local outbound and packets going from one interface to another
+ */
+static struct nf_hook_ops nss_connmgr_ipv6_ops_post_routing[] __read_mostly = {
+	{
+		.hook           = nss_connmgr_ipv6_post_routing_hook,
+		.owner          = THIS_MODULE,
+		.pf             = PF_INET6,
+		.hooknum        = NF_INET_POST_ROUTING,
+		.priority       = NF_IP6_PRI_NAT_SRC + 1 ,
+					/*
+					 * Refer to include/linux/netfiler_ipv6.h for priority levels.
+					 * Examine packets after NAT translation (and potentially any ALG processing)
+					 */
+	},
+};
+
+
+/*
+ * Expose what should be a static flag in the TCP connection tracker.
+ */
+extern int nf_ct_tcp_be_liberal;
+extern int nf_ct_tcp_no_window_check;
+
+static const struct file_operations nss_connmgr_ipv6_show_stats_ops = {
+	.open = simple_open,
+	.read = nss_connmgr_ipv6_read_stats,
+};
+
+static const struct file_operations nss_connmgr_ipv6_show_debug_stats_ops = {
+	.open = simple_open,
+	.read = nss_connmgr_ipv6_read_debug_stats,
+};
+
+static const struct file_operations nss_connmgr_ipv6_clear_stats_ops = {
+	.write = nss_connmgr_ipv6_clear_stats,
+};
+
+/*
+ * Network flow:
+ * HOST1-----------ROUTER------------HOST2
+ * HOST1 has IPa and MACa
+ * ROUTER has IPb and MACb facing HOST1 and IPc and MACc facing HOST2
+ * HOST2 has IPd and MACd
+ * i.e.
+ * HOST1-----------------ROUTER----------------HOST2
+ * IPa/MACa--------IPb/MACb:::IPc/MACc---------IPd/MACd
+ *
+ * Sending a packet from HOST1 to HOST2, the packet is mangled as follows (NAT'ed to HOST2):
+ * IPa/MACa---------->IPd/MACb:::::IPc/MACc---------->IPd/MACd
+ *
+ * Reply:
+ * IPa/MACa<----------IPd/MACb:::::IPc/MACc<----------IPd/MACd
+ *
+ * NOTE: IPx is considered to be IP addressing, protocol and port information combined.
+ */
+
+/*
+ * nss_connmgr_ipv6_mac_addr_get()
+ *	Return the hardware (MAC) address of the given ipv6 address, if any.
+ *
+ * Returns 0 on success or a negative result on failure.
+ * We look up the rtable entry for the address and, from its neighbour structure, obtain the hardware address.
+ * This means we will also work if the neighbours are routers too.
+ */
+static int nss_connmgr_ipv6_mac_addr_get(struct net *net, int oif, ipv6_addr_t addr, mac_addr_t mac_addr)
+{
+	struct neighbour *neigh;
+	struct rt6_info *rt6i;
+	struct dst_entry *dst;
+	struct in6_addr daddr;
+
+	/*
+	 * Look up the route to this address
+	 */
+	IPV6_ADDR_TO_IN6_ADDR(daddr, addr);
+	rt6i = rt6_lookup(net, &daddr, NULL, oif, 0);
+	if (!rt6i) {
+		NSS_CONNMGR_DEBUG_TRACE("rt6_lookup failed for: " IPV6_ADDR_OCTAL_FMT "\n", IPV6_ADDR_TO_OCTAL(addr));
+		return -1;
+	}
+
+	/*
+	 * refering to rt6_lookup an rt6_info is a subclass of dst_entry so we can cast it.
+	 */
+	dst = (struct dst_entry *)rt6i;
+
+	rcu_read_lock();
+
+	/*
+	 * Get the neighbour to which this destination is pointing
+	 */
+	neigh = dst_get_neighbour_noref(dst);
+	if (!neigh) {
+		rcu_read_unlock();
+		dst_release(dst);
+		NSS_CONNMGR_DEBUG_TRACE("Error: No DST reference");
+		return -2;
+	}
+
+	if (!(neigh->nud_state & NUD_VALID)) {
+		rcu_read_unlock();
+		dst_release(dst);
+		NSS_CONNMGR_DEBUG_TRACE("NUD Invalid");
+		return -3;
+	}
+	if (!neigh->dev) {
+		rcu_read_unlock();
+		dst_release(dst);
+		NSS_CONNMGR_DEBUG_TRACE("Neigh Dev Invalid");
+		return -4;
+	}
+	memcpy(mac_addr, neigh->ha, (size_t)neigh->dev->addr_len);
+	rcu_read_unlock();
+
+	dst_release(dst);
+
+	/*
+	 * If this mac looks like a multicast then it MAY be either truly multicast or it could be broadcast
+	 * Either way we fail!  We don't want to deal with multicasts or any sort because the NSS cannot deal with them.
+	 */
+	if (is_multicast_ether_addr(mac_addr)) {
+		NSS_CONNMGR_DEBUG_TRACE("Mac is multicast / broadcast - ignoring\n");
+		return -5;
+	}
+
+	return 0;
+}
+
+/*
+ * nss_connmgr_ipv6_post_routing_hook()
+ *	Called for packets about to leave the box - either locally generated or forwarded from another interface
+ */
+static unsigned int nss_connmgr_ipv6_post_routing_hook(unsigned int hooknum,
+		struct sk_buff *skb,
+		const struct net_device *in_unused,
+		const struct net_device *out,
+		int (*okfn)(struct sk_buff *))
+{
+	struct nss_ipv6_create unic;
+
+	struct net_device *in;
+	struct nf_conn *ct;
+	enum ip_conntrack_info ctinfo;
+	struct net_device *src_dev;
+	struct net_device *dest_dev;
+	struct nf_conntrack_tuple orig_tuple;
+	struct nf_conntrack_tuple reply_tuple;
+
+	nss_tx_status_t nss_tx_status;
+
+	/*
+	 * Don't process broadcast or multicast
+	 */
+	if (skb->pkt_type == PACKET_BROADCAST) {
+		NSS_CONNMGR_DEBUG_TRACE("Broadcast, ignoring: %p\n", skb);
+		return NF_ACCEPT;
+	}
+	if (skb->pkt_type == PACKET_MULTICAST) {
+		NSS_CONNMGR_DEBUG_TRACE("Multicast, ignoring: %p\n", skb);
+		return NF_ACCEPT;
+	}
+
+	/*
+	 * Only process packets being forwarded across the router - obtain the input interface for the skb
+	 * IMPORTANT 'in' must be released with dev_put(), this is not true for 'out'
+	 */
+	in = dev_get_by_index(&init_net, skb->skb_iif);
+	if  (!in) {
+		NSS_CONNMGR_DEBUG_TRACE("Not forwarded, ignoring: %p\n", skb);
+		return NF_ACCEPT;
+	}
+
+	/*
+	 * Only work with standard 802.3 mac address sizes
+	 */
+	if (in->addr_len != 6) {
+		dev_put(in);
+		NSS_CONNMGR_DEBUG_TRACE("in device (%s) not 802.3 hw addr len (%u), ignoring: %p\n", in->name, (unsigned)in->addr_len, skb);
+		return NF_ACCEPT;
+	}
+
+	if (out->addr_len != 6) {
+		dev_put(in);
+		NSS_CONNMGR_DEBUG_TRACE("out device (%s) not 802.3 hw addr len (%u), ignoring: %p\n", out->name, (unsigned)out->addr_len, skb);
+		return NF_ACCEPT;
+	}
+
+	/*
+	 * Only process packets that are also tracked by conntrack
+	 */
+	ct = nf_ct_get(skb, &ctinfo);
+	if (!ct) {
+		dev_put(in);
+		NSS_CONNMGR_DEBUG_TRACE("No conntrack connection, ignoring: %p\n", skb);
+		return NF_ACCEPT;
+	}
+	NSS_CONNMGR_DEBUG_TRACE("skb: %p tracked by connection: %p\n", skb, ct);
+
+	/*
+	 * Special untracked connection is not monitored
+	 */
+	if (ct == &nf_conntrack_untracked) {
+		dev_put(in);
+		NSS_CONNMGR_DEBUG_TRACE("%p: Untracked connection\n", ct);
+		return NF_ACCEPT;
+	}
+
+	/*
+	 * Any connection needing support from a 'helper' (aka NAT ALG) is kept away from the Network Accelerator
+	 */
+	if (nfct_help(ct)) {
+		dev_put(in);
+		NSS_CONNMGR_DEBUG_TRACE("%p: Connection has helper\n", ct);
+		return NF_ACCEPT;
+	}
+
+	/*
+	 * Now examine conntrack to identify the protocol, IP addresses and portal information involved
+	 * IMPORTANT: The information here will be as the 'ORIGINAL' direction, i.e. who established the connection.
+	 * This MAY NOT be the same as the current packet direction, for example, this packet direction may be eth1->eth0 but
+	 * originally the connection may be been started from a packet going from eth0->eth1.
+	 */
+	orig_tuple = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple;
+	reply_tuple = ct->tuplehash[IP_CT_DIR_REPLY].tuple;
+	unic.protocol = (int32_t)orig_tuple.dst.protonum;
+
+	/*
+	 * Get addressing information
+	 */
+	IN6_ADDR_TO_IPV6_ADDR(unic.src_ip, orig_tuple.src.u3.in6);
+	IN6_ADDR_TO_IPV6_ADDR(unic.dest_ip, orig_tuple.dst.u3.in6);
+
+	unic.flags = 0;
+
+	unic.return_pppoe_session_id = 0;
+	unic.flow_pppoe_session_id = 0;
+
+	switch (unic.protocol) {
+	case IPPROTO_TCP:
+		unic.src_port = (int32_t)orig_tuple.src.u.tcp.port;
+		unic.dest_port = (int32_t)orig_tuple.dst.u.tcp.port;
+		unic.flow_window_scale = ct->proto.tcp.seen[0].td_scale;
+		unic.flow_max_window = ct->proto.tcp.seen[0].td_maxwin;
+		unic.flow_end = ct->proto.tcp.seen[0].td_end;
+		unic.flow_max_end = ct->proto.tcp.seen[0].td_maxend;
+		unic.return_window_scale = ct->proto.tcp.seen[1].td_scale;
+		unic.return_max_window = ct->proto.tcp.seen[1].td_maxwin;
+		unic.return_end = ct->proto.tcp.seen[1].td_end;
+		unic.return_max_end = ct->proto.tcp.seen[1].td_maxend;
+		if ( nf_ct_tcp_be_liberal || nf_ct_tcp_no_window_check ||
+			(ct->proto.tcp.seen[0].flags & IP_CT_TCP_FLAG_BE_LIBERAL) ||
+			(ct->proto.tcp.seen[1].flags & IP_CT_TCP_FLAG_BE_LIBERAL)) {
+
+			unic.flags |= NSS_IPV6_CREATE_FLAG_NO_SEQ_CHECK;
+		}
+
+		/*
+		 * Don't try to manage a non-established connection.
+		 */
+		if (!test_bit(IPS_ASSURED_BIT, &ct->status)) {
+			dev_put(in);
+			NSS_CONNMGR_DEBUG_TRACE("%p: Non-established connection\n", ct);
+			return NF_ACCEPT;
+		}
+
+		/*
+		 * If the connection is shutting down do not manage it.
+		 * state can not be SYN_SENT, SYN_RECV because connection is assured
+		 * Not managed states: FIN_WAIT, CLOSE_WAIT, LAST_ACK, TIME_WAIT, CLOSE.
+		 */
+		spin_lock_bh(&ct->lock);
+		if (ct->proto.tcp.state != TCP_CONNTRACK_ESTABLISHED) {
+			spin_unlock_bh(&ct->lock);
+			dev_put(in);
+			NSS_CONNMGR_DEBUG_TRACE("%p: Connection in termination state %#X\n", ct, ct->proto.tcp.state);
+			return NF_ACCEPT;
+		}
+		spin_unlock_bh(&ct->lock);
+
+		break;
+
+	case IPPROTO_UDP:
+		unic.src_port = (int32_t)orig_tuple.src.u.udp.port;
+		unic.dest_port = (int32_t)orig_tuple.dst.u.udp.port;
+		break;
+
+	default:
+		/*
+		 * Streamengine compatibility - database stores non-ported protocols with port numbers equal to negative protocol number
+		 * to indicate unused.
+		 */
+		dev_put(in);
+		NSS_CONNMGR_DEBUG_TRACE("%p: Unhandled protocol %d\n", ct, unic.protocol);
+		return NF_ACCEPT;
+	}
+
+	/*
+	 * Get MAC addresses
+	 * NOTE: We are dealing with the ORIGINAL direction here so 'in' and 'out' dev may need
+	 * to be swapped if this packet is a reply
+	 */
+	if (ctinfo < IP_CT_IS_REPLY) {
+		src_dev = in;
+		dest_dev = (struct net_device *)out;
+		NSS_CONNMGR_DEBUG_TRACE("%p: dir: Original\n", ct);
+	} else {
+		src_dev = (struct net_device *)out;
+		dest_dev = in;
+		NSS_CONNMGR_DEBUG_TRACE("%p: dir: Reply\n", ct);
+	}
+
+	/*
+	 * Get the MAC addresses that correspond to source and destination host addresses.
+	 */
+	if (nss_connmgr_ipv6_mac_addr_get(dev_net(src_dev), src_dev->ifindex, unic.src_ip, unic.src_mac)) {
+		dev_put(in);
+		NSS_CONNMGR_DEBUG_TRACE("%p: Failed to find MAC address for src IP: %pI6\n", ct, &unic.src_ip);
+		return NF_ACCEPT;
+	}
+
+	/*
+	 * Do dest now
+	 */
+	if (nss_connmgr_ipv6_mac_addr_get(dev_net(dest_dev), dest_dev->ifindex, unic.dest_ip, unic.dest_mac)) {
+		dev_put(in);
+		NSS_CONNMGR_DEBUG_TRACE("%p: Failed to find MAC address for dest IP: %pI6\n", ct, &unic.dest_ip);
+		return NF_ACCEPT;
+	}
+
+	/*
+	 * Only devices that are NSS devices may be accelerated.
+	 */
+	unic.src_interface_num = nss_get_interface_number(nss_connmgr_ipv6.nss_context, src_dev);
+	if (unic.src_interface_num < 0) {
+		dev_put(in);
+		return NF_ACCEPT;
+	}
+
+	unic.dest_interface_num = nss_get_interface_number(nss_connmgr_ipv6.nss_context, dest_dev);
+	if (unic.dest_interface_num < 0) {
+		dev_put(in);
+		return NF_ACCEPT;
+	}
+
+	/*
+	 * Get MTU values for source and destination interfaces.
+	 */
+	unic.from_mtu = in->mtu;
+	unic.to_mtu = out->mtu;
+
+	/*
+	 * We have everything we need (hopefully :-])
+	 */
+	NSS_CONNMGR_DEBUG_TRACE("\n%p: Conntrack connection\n"
+			"skb: %p\n"
+			"dir: %s\n"
+			"Protocol: %d\n"
+			"src_ip: " IPV6_ADDR_OCTAL_FMT ":%d\n"
+			"dest_ip: " IPV6_ADDR_OCTAL_FMT ":%d\n"
+			"src_mac: " MAC_FMT "\n"
+			"dest_mac: " MAC_FMT "\n"
+			"src_dev: %s\n"
+			"dest_dev: %s\n"
+			"src_iface_num: %u\n"
+			"dest_iface_num: %u\n",
+			ct,
+			skb,
+			(ctinfo < IP_CT_IS_REPLY)? "Original" : "Reply",
+			unic.protocol,
+			IPV6_ADDR_TO_OCTAL(unic.src_ip), unic.src_port,
+			IPV6_ADDR_TO_OCTAL(unic.dest_ip), unic.dest_port,
+			MAC_AS_BYTES(unic.src_mac),
+			MAC_AS_BYTES(unic.dest_mac),
+			src_dev->name,
+			dest_dev->name,
+			unic.src_interface_num,
+			unic.dest_interface_num);
+
+	/*
+	 * Create the Network Accelerator connection cache entries
+	 *
+	 * NOTE: All of the information we have is from the point of view of who created the connection however
+	 * the skb may actually be in the 'reply' direction - which is important to know when configuring the NSS as we have to set the right information
+	 * on the right "match" and "forwarding" entries.
+	 * We can use the ctinfo to determine which direction the skb is in and then swap fields as necessary.
+	 */
+	IPV6_ADDR_NTOH(unic.src_ip, unic.src_ip);
+	IPV6_ADDR_NTOH(unic.dest_ip, unic.dest_ip);
+	unic.src_port = ntohs(unic.src_port);
+	unic.dest_port = ntohs(unic.dest_port);
+	/*
+	 * If operations have stopped then do not process packets
+	 */
+	spin_lock_bh(&nss_connmgr_ipv6.lock);
+	if (unlikely(nss_connmgr_ipv6.stopped)) {
+		spin_unlock_bh(&nss_connmgr_ipv6.lock);
+		dev_put(in);
+		NSS_CONNMGR_DEBUG_TRACE("Stopped, ignoring: %p\n", skb);
+
+		return NF_ACCEPT;
+	}
+	spin_unlock_bh(&nss_connmgr_ipv6.lock);
+	nss_tx_status = nss_tx_create_ipv6_rule(nss_connmgr_ipv6.nss_context, &unic);
+
+	if (nss_tx_status == NSS_TX_SUCCESS) {
+		goto out;
+	} else if (nss_tx_status == NSS_TX_FAILURE_NOT_READY) {
+		NSS_CONNMGR_DEBUG_ERROR("NSS not ready to accept rule \n");
+		spin_lock_bh(&nss_connmgr_ipv6.lock);
+		nss_connmgr_ipv6.debug_stats[NSS_CONNMGR_IPV6_CREATE_FAIL]++;
+		spin_unlock_bh(&nss_connmgr_ipv6.lock);
+	} else {
+		NSS_CONNMGR_DEBUG_TRACE("NSS create rule failed  skb: %p\n", skb);
+		spin_lock_bh(&nss_connmgr_ipv6.lock);
+		nss_connmgr_ipv6.debug_stats[NSS_CONNMGR_IPV6_CREATE_FAIL]++;
+		spin_unlock_bh(&nss_connmgr_ipv6.lock);
+	}
+
+out:
+	/*
+	 * Release the interface on which this skb arrived
+	 */
+	dev_put(in);
+
+	return NF_ACCEPT;
+}
+
+/*
+ * nss_connmgr_ipv6_net_dev_callback()
+ *	Callback handler from the linux device driver for the Network Accelerator.
+ *
+ * The NSS passes sync and stats information to the device driver - which then passes them
+ * onto this callback.
+ * This arrangement means that dependencies on driver and conntrack modules are solely in this module.
+ * These callbacks aim to keep conntrack connections alive and to ensure that connection
+ * state - especially for TCP connections that track sequence space for example - is kept in synchronisation
+ * before packets flow back through linux from a period of NSS processing.
+ */
+static void nss_connmgr_ipv6_net_dev_callback(struct nss_ipv6_cb_params *nicp)
+{
+	struct nf_conntrack_tuple_hash *h;
+	struct nf_conntrack_tuple tuple;
+	struct nf_conn *ct;
+	struct nf_conn_counter *acct;
+	struct nss_connmgr_ipv6_connection *connection;
+
+	struct neighbour *neigh;
+
+	struct nss_ipv6_sync *sync;
+	struct nss_ipv6_establish *establish;
+
+	struct net_device *flow_dev;
+	struct net_device *return_dev;
+
+	uint32_t nd_key[4];
+
+	switch(nicp->reason)
+	{
+		case NSS_IPV6_CB_REASON_ESTABLISH:
+			establish = &nicp->params.establish;
+
+			if (unlikely(establish->index >= NSS_CONNMGR_IPV6_CONN_MAX)) {
+				NSS_CONNMGR_DEBUG_TRACE("Bad establish index: %d\n", establish->index);
+				return;
+			}
+
+			connection = &nss_connmgr_ipv6.connection[establish->index];
+
+			if (unlikely(connection->state == NSS_CONNMGR_IPV6_STATE_ESTABLISHED)) {
+				NSS_CONNMGR_DEBUG_WARN("Invalid sync callback. Conn already established : %d \n", establish->index);
+				return;
+			}
+			NSS_CONNMGR_DEBUG_TRACE("NSS ipv6 Establish callback %d \n", establish->index);
+
+			spin_lock_bh(&nss_connmgr_ipv6.lock);
+
+			connection->state = NSS_CONNMGR_IPV6_STATE_ESTABLISHED;
+
+			connection->protocol = establish->protocol;
+
+			connection->src_interface = establish->flow_interface;
+			connection->src_addr[0] = establish->flow_ip[0];
+			connection->src_addr[1] = establish->flow_ip[1];
+			connection->src_addr[2] = establish->flow_ip[2];
+			connection->src_addr[3] = establish->flow_ip[3];
+			connection->src_port = establish->flow_ident;
+
+			connection->dest_interface = establish->return_interface;
+			connection->dest_addr[0] = establish->return_ip[0];
+			connection->dest_addr[1] = establish->return_ip[1];
+			connection->dest_addr[2] = establish->return_ip[2];
+			connection->dest_addr[3] = establish->return_ip[3];
+			connection->dest_port = establish->return_ident;
+
+			connection->stats[NSS_CONNMGR_IPV6_ACCELERATED_RX_PKTS] = 0;
+			connection->stats[NSS_CONNMGR_IPV6_ACCELERATED_RX_BYTES] = 0;
+			connection->stats[NSS_CONNMGR_IPV6_ACCELERATED_TX_PKTS] = 0;
+			connection->stats[NSS_CONNMGR_IPV6_ACCELERATED_TX_BYTES] = 0;
+
+			spin_unlock_bh(&nss_connmgr_ipv6.lock);
+
+			return;
+
+		case NSS_IPV6_CB_REASON_SYNC:
+			sync = &nicp->params.sync;
+
+			if (unlikely(sync->index >= NSS_CONNMGR_IPV6_CONN_MAX)) {
+				NSS_CONNMGR_DEBUG_TRACE("Bad sync index: %d\n", sync->index);
+				return;
+			}
+
+			connection = &nss_connmgr_ipv6.connection[sync->index];
+
+			NSS_CONNMGR_DEBUG_TRACE("NSS ipv6 Sync callback %d \n", sync->index);
+
+			if (unlikely(connection->state != NSS_CONNMGR_IPV6_STATE_ESTABLISHED)) {
+				NSS_CONNMGR_DEBUG_WARN("Invalid sync callback. Conn not established : %d \n", sync->index);
+				return;
+			}
+
+			break;
+
+		default:
+			NSS_CONNMGR_DEBUG_WARN("%d: Unhandled callback\n", nicp->reason);
+			return;
+	}
+
+	if (sync->final_sync) {
+		spin_lock_bh(&nss_connmgr_ipv6.lock);
+		connection->state = NSS_CONNMGR_IPV6_STATE_INACTIVE;
+		spin_unlock_bh(&nss_connmgr_ipv6.lock);
+	} else {
+		spin_lock_bh(&nss_connmgr_ipv6.lock);
+		connection->stats[NSS_CONNMGR_IPV6_ACCELERATED_RX_PKTS] += sync->flow_packet_count;
+		connection->stats[NSS_CONNMGR_IPV6_ACCELERATED_RX_BYTES] += sync->flow_byte_count;
+		connection->stats[NSS_CONNMGR_IPV6_ACCELERATED_TX_PKTS]+= sync->return_packet_count;
+		connection->stats[NSS_CONNMGR_IPV6_ACCELERATED_TX_BYTES] += sync->return_byte_count;
+		spin_unlock_bh(&nss_connmgr_ipv6.lock);
+
+	}
+
+	/*
+	 * Create a tuple so as to be able to look up a connection
+	 */
+	memset(&tuple, 0, sizeof(tuple));
+	IPV6_ADDR_NTOH_IN6_ADDR(tuple.src.u3.in6, connection->src_addr);
+	tuple.src.u.all = ntohs((__be16)connection->src_port);
+	tuple.src.l3num = AF_INET6;
+
+	IPV6_ADDR_NTOH_IN6_ADDR(tuple.dst.u3.in6, connection->dest_addr);
+	tuple.dst.u.all = ntohs((__be16)connection->dest_port);
+	tuple.dst.dir = IP_CT_DIR_ORIGINAL;
+
+	tuple.dst.protonum = (uint8_t)connection->protocol;
+
+	NSS_CONNMGR_DEBUG_TRACE("\nNSS Update, lookup connection using\n"
+			"Protocol: %d\n"
+			"src_addr: %pI6:%d\n"
+			"dest_addr: %pI6:%d\n",
+			(int)tuple.dst.protonum,
+			&(tuple.src.u3.in6), (int)tuple.src.u.all,
+			&(tuple.dst.u3.in6), (int)tuple.dst.u.all);
+
+	/*
+	 * Look up conntrack connection
+	 */
+	h = nf_conntrack_find_get(&init_net, NF_CT_DEFAULT_ZONE, &tuple);
+	if (!h) {
+		NSS_CONNMGR_DEBUG_WARN("Conntrack could not be found in sync");
+		return;
+	}
+
+	ct = nf_ct_tuplehash_to_ctrack(h);
+	NF_CT_ASSERT(ct->timeout.data == (unsigned long)ct);
+
+	/*
+	 * Only update if this is not a fixed timeout
+	 */
+	if (!test_bit(IPS_FIXED_TIMEOUT_BIT, &ct->status)) {
+		ct->timeout.expires += sync->delta_jiffies;
+	}
+
+	acct = nf_conn_acct_find(ct);
+	if (acct) {
+		spin_lock_bh(&ct->lock);
+		atomic64_add(sync->flow_packet_count, &acct[IP_CT_DIR_ORIGINAL].packets);
+		atomic64_add(sync->flow_byte_count, &acct[IP_CT_DIR_ORIGINAL].bytes);
+
+		atomic64_add(sync->return_packet_count, &acct[IP_CT_DIR_REPLY].packets);
+		atomic64_add(sync->return_byte_count, &acct[IP_CT_DIR_REPLY].bytes);
+		spin_unlock_bh(&ct->lock);
+	}
+
+	switch (connection->protocol) {
+	case IPPROTO_TCP:
+		spin_lock_bh(&ct->lock);
+		if (ct->proto.tcp.seen[0].td_maxwin < sync->flow_max_window) {
+			ct->proto.tcp.seen[0].td_maxwin = sync->flow_max_window;
+		}
+		if ((int32_t)(ct->proto.tcp.seen[0].td_end - sync->flow_end) < 0) {
+			ct->proto.tcp.seen[0].td_end = sync->flow_end;
+		}
+		if ((int32_t)(ct->proto.tcp.seen[0].td_maxend - sync->flow_max_end) < 0) {
+			ct->proto.tcp.seen[0].td_maxend = sync->flow_max_end;
+		}
+		if (ct->proto.tcp.seen[1].td_maxwin < sync->return_max_window) {
+			ct->proto.tcp.seen[1].td_maxwin = sync->return_max_window;
+		}
+		if ((int32_t)(ct->proto.tcp.seen[1].td_end - sync->return_end) < 0) {
+			ct->proto.tcp.seen[1].td_end = sync->return_end;
+		}
+		if ((int32_t)(ct->proto.tcp.seen[1].td_maxend - sync->return_max_end) < 0) {
+			ct->proto.tcp.seen[1].td_maxend = sync->return_max_end;
+		}
+		spin_unlock_bh(&ct->lock);
+		break;
+	}
+
+	flow_dev = nss_get_interface_dev(nss_connmgr_ipv6.nss_context, connection->src_interface);
+	if (unlikely(!flow_dev)) {
+		NSS_CONNMGR_DEBUG_WARN("Invalid src dev reference from NSS \n");
+		goto out;
+	}
+
+	return_dev = nss_get_interface_dev(nss_connmgr_ipv6.nss_context, connection->dest_interface);
+	if (unlikely(!return_dev)) {
+		NSS_CONNMGR_DEBUG_WARN("Invalid dest dev reference from NSS \n");
+		goto out;
+	}
+
+	/*
+	 * Hold the net_device references
+	 */
+	dev_hold(flow_dev);
+	dev_hold(return_dev);
+
+	/*
+	 * Update ND table.
+	 */
+	IPV6_ADDR_NTOH(nd_key, connection->src_addr);
+	neigh = neigh_lookup(&nd_tbl, &nd_key, flow_dev);
+	if (neigh) {
+		neigh_update(neigh, NULL, neigh->nud_state, NEIGH_UPDATE_F_WEAK_OVERRIDE);
+		neigh_release(neigh);
+	} else {
+		NSS_CONNMGR_DEBUG_TRACE("Neighbour entry could not be found for flow_dev\n");
+	}
+
+	IPV6_ADDR_NTOH(nd_key, connection->dest_addr);
+	neigh = neigh_lookup(&nd_tbl, &nd_key, return_dev);
+	if (neigh) {
+		neigh_update(neigh, NULL, neigh->nud_state, NEIGH_UPDATE_F_WEAK_OVERRIDE);
+		neigh_release(neigh);
+	} else {
+		NSS_CONNMGR_DEBUG_TRACE("Neighbour entry could not be found for return_dev\n");
+	}
+
+	/*
+	 * Release the net_device references
+	 */
+	dev_put(flow_dev);
+	dev_put(return_dev);
+
+out:
+	/*
+	 * Release connection
+	 */
+	nf_ct_put(ct);
+
+	return;
+}
+
+#ifdef CONFIG_NF_CONNTRACK_EVENTS
+/*
+ * nss_connmgr_ipv6_conntrack_event()
+ *	Callback event invoked when conntrack connection state changes, currently we handle destroy events to quickly release state
+ */
+#ifdef CONFIG_NF_CONNTRACK_CHAIN_EVENTS
+static int nss_connmgr_ipv6_conntrack_event(struct notifier_block *this, unsigned long events, void *ptr)
+#else
+static int nss_connmgr_ipv6_conntrack_event(unsigned int events, struct nf_ct_event *item)
+#endif
+{
+	struct nss_ipv6_destroy unid;
+#ifdef CONFIG_NF_CONNTRACK_CHAIN_EVENTS
+	struct nf_ct_event *item = (struct nf_ct_event *)ptr;
+#endif
+	struct nf_conn *ct = item->ct;
+	struct nf_conntrack_tuple orig_tuple;
+	struct nf_conntrack_tuple reply_tuple;
+
+	nss_tx_status_t nss_tx_status;
+
+	/*
+	 * Basic sanity checks on the event
+	 */
+	if (!ct) {
+		NSS_CONNMGR_DEBUG_WARN("No ct in conntrack event callback\n");
+		return NOTIFY_DONE;
+	}
+	if (ct == &nf_conntrack_untracked) {
+		NSS_CONNMGR_DEBUG_TRACE("%p: ignoring untracked connn", ct);
+		return NOTIFY_DONE;
+	}
+
+	/*
+	 * Only interested if this is ipv6
+	 */
+	if (nf_ct_l3num(ct) != AF_INET6) {
+		return NOTIFY_DONE;
+	}
+
+	/*
+	 * Only interested in destroy events
+	 */
+	if (!(events & (1 << IPCT_DESTROY))) {
+		return NOTIFY_DONE;
+	}
+
+	/*
+	 * Now examine conntrack to identify the protocol, IP addresses and portal information involved
+	 * IMPORTANT: The information here will be as the 'ORIGINAL' direction, i.e. who established the connection.
+	 * This MAY NOT be the same as the current packet direction, for example, this packet direction may be eth1->eth0 but
+	 * originally the connection may be been started from a packet going from eth0->eth1.
+	 */
+	orig_tuple = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple;
+	reply_tuple = ct->tuplehash[IP_CT_DIR_REPLY].tuple;
+	unid.protocol = (int32_t)orig_tuple.dst.protonum;
+
+	/*
+	 * Get addressing information, non-NAT first
+	 */
+	IN6_ADDR_TO_IPV6_ADDR(unid.src_ip, orig_tuple.src.u3.in6);
+	IN6_ADDR_TO_IPV6_ADDR(unid.dest_ip, orig_tuple.dst.u3.in6);
+
+	switch (unid.protocol) {
+	case IPPROTO_TCP:
+		unid.src_port = (int32_t)orig_tuple.src.u.tcp.port;
+		unid.dest_port = (int32_t)orig_tuple.dst.u.tcp.port;
+		break;
+
+	case IPPROTO_UDP:
+		unid.src_port = (int32_t)orig_tuple.src.u.udp.port;
+		unid.dest_port = (int32_t)orig_tuple.dst.u.udp.port;
+		break;
+
+	default:
+		/*
+		 * Database stores non-ported protocols with port numbers equal to negative protocol number
+		 * to indicate unused.
+		 */
+		NSS_CONNMGR_DEBUG_TRACE("%p: Unhandled protocol %d\n", ct, unid.protocol);
+		unid.src_port = -unid.protocol;
+		unid.dest_port = -unid.protocol;
+	}
+
+
+	/*
+	 * Only deal with TCP or UDP
+	 */
+	if ((unid.protocol != IPPROTO_TCP) && (unid.protocol != IPPROTO_UDP)) {
+		return NOTIFY_DONE;
+	}
+
+	NSS_CONNMGR_DEBUG_TRACE("\n%p: Connection destroyed\n"
+			"Protocol: %d\n"
+			"src_ip: " IPV6_ADDR_OCTAL_FMT ":%d\n"
+			"dest_ip: " IPV6_ADDR_OCTAL_FMT ":%d\n",
+			ct,
+			unid.protocol,
+			IPV6_ADDR_TO_OCTAL(unid.src_ip), unid.src_port,
+			IPV6_ADDR_TO_OCTAL(unid.dest_ip), unid.dest_port);
+
+	/*
+	 * Destroy the Network Accelerator connection cache entries.
+	 */
+	unid.src_ip[0] = ntohl(unid.src_ip[0]);
+	unid.src_ip[1] = ntohl(unid.src_ip[1]);
+	unid.src_ip[2] = ntohl(unid.src_ip[2]);
+	unid.src_ip[3] = ntohl(unid.src_ip[3]);
+	unid.dest_ip[0] = ntohl(unid.dest_ip[0]);
+	unid.dest_ip[1] = ntohl(unid.dest_ip[1]);
+	unid.dest_ip[2] = ntohl(unid.dest_ip[2]);
+	unid.dest_ip[3] = ntohl(unid.dest_ip[3]);
+	unid.src_port = ntohs(unid.src_port);
+	unid.dest_port = ntohs(unid.dest_port);
+	/*
+	 * Destroy the Network Accelerator connection cache entries.
+	 */
+	nss_tx_status = nss_tx_destroy_ipv6_rule(nss_connmgr_ipv6.nss_context, &unid);
+	if (nss_tx_status == NSS_TX_SUCCESS) {
+		goto out;
+	} else if (nss_tx_status == NSS_TX_FAILURE_NOT_READY) {
+		NSS_CONNMGR_DEBUG_ERROR("NSS not ready to accept 'destroy IPv6' rule \n");
+		spin_lock_bh(&nss_connmgr_ipv6.lock);
+		nss_connmgr_ipv6.debug_stats[NSS_CONNMGR_IPV6_DESTROY_FAIL]++;
+		spin_unlock_bh(&nss_connmgr_ipv6.lock);
+	} else {
+		NSS_CONNMGR_DEBUG_TRACE("NSS destroy rule fail \n");
+		spin_lock_bh(&nss_connmgr_ipv6.lock);
+		nss_connmgr_ipv6.debug_stats[NSS_CONNMGR_IPV6_DESTROY_FAIL]++;
+		spin_unlock_bh(&nss_connmgr_ipv6.lock);
+	}
+out:
+	return NOTIFY_DONE;
+}
+
+#endif
+/*
+ * nss_connmgr_ipv6_init_connection_table()
+ *      initialize connection table
+ */
+static void nss_connmgr_ipv6_init_connection_table(void)
+{
+	int32_t i,j;
+
+	spin_lock_bh(&nss_connmgr_ipv6.lock);
+	for (i = 0; (i < NSS_CONNMGR_IPV6_CONN_MAX); i++) {
+		nss_connmgr_ipv6.connection[i].state = NSS_CONNMGR_IPV6_STATE_INACTIVE;
+
+		for (j = 0; (j < NSS_CONNMGR_IPV6_STATS_MAX); j++) {
+			nss_connmgr_ipv6.connection[i].stats[j] = 0;
+		}
+	}
+	spin_unlock_bh(&nss_connmgr_ipv6.lock);
+
+	return;
+}
+
+/*
+ * nss_connmgr_ipv6_get_terminate()
+ */
+static ssize_t nss_connmgr_ipv6_get_terminate(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	ssize_t count;
+	int num;
+
+
+	/*
+	 * Operate under our locks
+	 */
+	spin_lock_bh(&nss_connmgr_ipv6.lock);
+	num = nss_connmgr_ipv6.terminate;
+	spin_unlock_bh(&nss_connmgr_ipv6.lock);
+
+	count = snprintf(buf, (ssize_t)PAGE_SIZE, "%d\n", num);
+	return count;
+}
+
+/*
+ * nss_connmgr_ipv6_set_terminate()
+ *	Writing anything to this 'file' will cause the default classifier to terminate
+ */
+static ssize_t nss_connmgr_ipv6_set_terminate(struct device *dev,
+				struct device_attribute *attr,
+				const char *buf, size_t count)
+{
+
+
+	NSS_CONNMGR_DEBUG_INFO("Terminate\n");
+	spin_lock_bh(&nss_connmgr_ipv6.lock);
+	nss_connmgr_ipv6.terminate = 1;
+	wake_up_process(nss_connmgr_ipv6.thread);
+	spin_unlock_bh(&nss_connmgr_ipv6.lock);
+
+	return count;
+}
+
+/*
+ * nss_connmgr_ipv6_get_stop()
+ */
+static ssize_t nss_connmgr_ipv6_get_stop(struct device *dev,
+				struct device_attribute *attr,
+				char *buf)
+{
+	ssize_t count;
+	int num;
+
+	/*
+	 * Operate under our locks
+	 */
+	spin_lock_bh(&nss_connmgr_ipv6.lock);
+	num = nss_connmgr_ipv6.stopped;
+	spin_unlock_bh(&nss_connmgr_ipv6.lock);
+
+	count = snprintf(buf, (ssize_t)PAGE_SIZE, "%d\n", num);
+	return count;
+}
+
+/*
+ * nss_connmgr_ipv6_set_stop()
+ */
+static ssize_t nss_connmgr_ipv6_set_stop(struct device *dev,
+					struct device_attribute *attr,
+					const char *buf, size_t count)
+{
+	char num_buf[12];
+	int num;
+
+	/*
+	 * Get the number from buf into a properly z-termed number buffer
+	 */
+	if (count > 11) {
+		return 0;
+	}
+	memcpy(num_buf, buf, count);
+	num_buf[count] = '\0';
+	sscanf(num_buf, "%d", &num);
+	NSS_CONNMGR_DEBUG_TRACE("nss_connmgr_ipv6_stop = %d\n", num);
+
+	/*
+	 * Operate under our locks and stop further processing of packets
+	 */
+	spin_lock_bh(&nss_connmgr_ipv6.lock);
+	nss_connmgr_ipv6.stopped = num;
+	spin_unlock_bh(&nss_connmgr_ipv6.lock);
+
+	return count;
+}
+
+/*
+ * SysFS attributes for the default classifier itself.
+ */
+static const struct device_attribute nss_connmgr_ipv6_terminate_attr =
+		__ATTR(terminate, S_IWUGO | S_IRUGO, nss_connmgr_ipv6_get_terminate, nss_connmgr_ipv6_set_terminate);
+static const struct device_attribute nss_connmgr_ipv6_stop_attr =
+		__ATTR(stop, S_IWUGO | S_IRUGO, nss_connmgr_ipv6_get_stop, nss_connmgr_ipv6_set_stop);
+
+/*
+ * nss_connmgr_ipv6_thread_fn()
+ *	A thread to handle tasks that can only be done in thread context.
+ */
+static int nss_connmgr_ipv6_thread_fn(void *arg)
+{
+	int result = 0;
+
+
+	NSS_CONNMGR_DEBUG_INFO("NSS Connection manager thread start\n");
+
+	/*
+	 * Get reference to this module - we release it when the thread exits
+	 */
+	try_module_get(THIS_MODULE);
+
+	/*
+	 * Create /sys/nom_v6
+	 */
+	nss_connmgr_ipv6.nom_v6 = kobject_create_and_add("nom_v6", NULL);
+	if (unlikely(nss_connmgr_ipv6.nom_v6 == NULL)) {
+		NSS_CONNMGR_DEBUG_ERROR("Failed to register nom_v6\n");
+		goto task_cleanup_1;
+	}
+
+	/*
+	 * Create files, one for each parameter supported by this module
+	 */
+	result = sysfs_create_file(nss_connmgr_ipv6.nom_v6, &nss_connmgr_ipv6_terminate_attr.attr);
+	if (result) {
+		NSS_CONNMGR_DEBUG_ERROR("Failed to register terminate file %d\n", result);
+		goto task_cleanup_2;
+	}
+
+	/*
+	 * Register netfilter hooks - ipv6
+	 */
+	result = nf_register_hooks(nss_connmgr_ipv6_ops_post_routing, ARRAY_SIZE(nss_connmgr_ipv6_ops_post_routing));
+	if (result < 0) {
+		NSS_CONNMGR_DEBUG_ERROR("Can't register nf post routing hook %d\n", result);
+		goto task_cleanup_3;
+	}
+
+	result = sysfs_create_file(nss_connmgr_ipv6.nom_v6, &nss_connmgr_ipv6_stop_attr.attr);
+	if (result) {
+		NSS_CONNMGR_DEBUG_ERROR("Failed to register stop file %d\n", result);
+		goto task_cleanup_4;
+	}
+
+#ifdef CONFIG_NF_CONNTRACK_EVENTS
+	/*
+	 * Eventing subsystem is available so we register a notifier hook to get fast notifications of expired connections
+	 */
+	/*
+	 * TODO Current we are just ignoring the error case where notifier cannot be registered.
+	 * No conntrack destroy events will be recieved in this case and hence destroy rule command
+	 * will never be sent to NSS. This will be fixed (by moving to netlink based conntrack notification */
+
+	result = nf_conntrack_register_notifier(&init_net, &nss_connmgr_ipv6.conntrack_notifier);
+	if (result < 0) {
+		NSS_CONNMGR_DEBUG_TRACE("Can't register nf notifier hook %d\n", result);
+	}
+#endif
+
+	/*
+	 * Register this module with the Linux NSS driver (net_device)
+	 */
+	nss_connmgr_ipv6.nss_context = nss_register_ipv6_mgr(nss_connmgr_ipv6_net_dev_callback);
+
+	/*
+	 * Initialize connection table
+	 */
+	nss_connmgr_ipv6_init_connection_table();
+
+	/*
+	 * Allow wakeup signals
+	 */
+	allow_signal(SIGCONT);
+	spin_lock_bh(&nss_connmgr_ipv6.lock);
+	while (!nss_connmgr_ipv6.terminate) {
+		/*
+		 * Sleep and wait for an instruction
+		 */
+		spin_unlock_bh(&nss_connmgr_ipv6.lock);
+		NSS_CONNMGR_DEBUG_TRACE("nss_connmgr_ipv6 sleep\n");
+		set_current_state(TASK_INTERRUPTIBLE);
+		schedule();
+		spin_lock_bh(&nss_connmgr_ipv6.lock);
+	}
+	spin_unlock_bh(&nss_connmgr_ipv6.lock);
+	NSS_CONNMGR_DEBUG_INFO("nss_connmgr_ipv6 terminate\n");
+
+	result = 0;
+
+	nss_unregister_ipv6_mgr();
+
+#ifdef CONFIG_NF_CONNTRACK_EVENTS
+	if (!result) {
+		nf_conntrack_unregister_notifier(&init_net, &nss_connmgr_ipv6.conntrack_notifier);
+	}
+#endif
+	sysfs_remove_file(nss_connmgr_ipv6.nom_v6, &nss_connmgr_ipv6_stop_attr.attr);
+task_cleanup_4:
+	nf_unregister_hooks(nss_connmgr_ipv6_ops_post_routing, ARRAY_SIZE(nss_connmgr_ipv6_ops_post_routing));
+task_cleanup_3:
+	sysfs_remove_file(nss_connmgr_ipv6.nom_v6, &nss_connmgr_ipv6_terminate_attr.attr);
+task_cleanup_2:
+	kobject_put(nss_connmgr_ipv6.nom_v6);
+task_cleanup_1:
+
+	module_put(THIS_MODULE);
+	return result;
+}
+
+/*
+ * nss_connmgr_ipv6_read_stats
+ *      Read ipv6 stats
+ */
+static ssize_t nss_connmgr_ipv6_read_stats(struct file *fp, char __user *ubuf, size_t sz, loff_t *ppos)
+{
+	int32_t i;
+	int32_t j;
+	int32_t k;
+	size_t size_al = NSS_CONNMGR_IPV6_DEBUGFS_BUF_SZ;
+	size_t size_wr = 0;
+	ssize_t bytes_read = 0;
+
+	/*
+	 * Temporary array to hold statistics for printing. To avoid holding
+	 * spinlocks for long time while printing, we copy the stats to this
+	 * temporary buffer while holding the spinlock, unlock and then print.
+	 * Also to avoid using big chunk of memory on stack, we use an array of
+	 * only 8 connections, and print stats for 8 connections at a time
+	 */
+	uint64_t stats[8][NSS_CONNMGR_IPV6_STATS_MAX];
+	nss_connmgr_ipv6_conn_state_t  state[8];
+
+	char *lbuf = kzalloc(size_al, GFP_KERNEL);
+	if (unlikely(lbuf == NULL)) {
+		NSS_CONNMGR_DEBUG_WARN("Could not allocate memory for local statistics buffer \n");
+		return 0;
+	}
+
+	/*
+	 * TODO : Handle buffer full conditions by putting some checks , so it
+	 * doesn't crash or print something rubbish
+	 */
+	size_wr = scnprintf(lbuf, size_al,"connection mgr ipv6 stats :\n\n");
+
+	for (i = 0; (i < NSS_CONNMGR_IPV6_CONN_MAX) && (size_wr < size_al) ; i+=8) {
+
+		/* 1. Take a lock */
+		spin_lock_bh(&nss_connmgr_ipv6.lock);
+
+		/* 2. Copy stats for 8 connections into local buffer */
+		for (j = 0; j < 8; j++) {
+			state[j] = nss_connmgr_ipv6.connection[i+j].state;
+		}
+		memcpy(stats, nss_connmgr_ipv6.connection[i].stats, sizeof(stats));
+
+		/* 3. Release the lock */
+		spin_unlock_bh(&nss_connmgr_ipv6.lock);
+
+		/* 4. Print stats for 8 connections */
+		for (j = 0; (j < 8); j++)
+		{
+			size_wr += scnprintf(lbuf + size_wr, size_al - size_wr,
+					"------------  Connection %d ( %s ) ---------------\n",
+					(i+j), nss_connmgr_ipv6_conn_state_str[state[j]]);
+			for (k = 0; (k < NSS_CONNMGR_IPV6_STATS_MAX); k++) {
+				size_wr += scnprintf(lbuf + size_wr, size_al - size_wr,
+						"%s = %llu\n", nss_connmgr_ipv6_conn_stats_str[k], stats[j][k]);
+			}
+		}
+	}
+
+	if (size_wr == size_al) {
+		NSS_CONNMGR_DEBUG_WARN("Print Buffer not available for debug stats \n");
+	}
+	else {
+		size_wr += scnprintf(lbuf + size_wr, size_al - size_wr,"\nconnection mgr stats end\n\n");
+	}
+
+	bytes_read = simple_read_from_buffer(ubuf, sz, ppos, lbuf, strlen(lbuf));
+	kfree(lbuf);
+
+	return bytes_read;
+}
+
+/*
+ * nss_connmgr_ipv6_read_debug_stats
+ *	Read connection manager debug stats
+ */
+static ssize_t nss_connmgr_ipv6_read_debug_stats(struct file *fp, char __user *ubuf, size_t sz, loff_t *ppos)
+{
+	int32_t i;
+
+	/*
+	 * max output lines = #stats + start tag line + end tag line + three blank lines
+	 */
+	uint32_t max_output_lines = NSS_CONNMGR_IPV6_DEBUG_STATS_MAX + 5;
+	size_t size_al = NSS_CONNMGR_IPV6_MAX_STR_LENGTH * max_output_lines;
+	size_t size_wr = 0;
+	ssize_t bytes_read = 0;
+	uint32_t stats_shadow[NSS_CONNMGR_IPV6_DEBUG_STATS_MAX];
+
+	char *lbuf = kzalloc(size_al, GFP_KERNEL);
+	if (unlikely(lbuf == NULL)) {
+		NSS_CONNMGR_DEBUG_WARN("Could not allocate memory for local statistics buffer");
+		return 0;
+	}
+
+	size_wr = scnprintf(lbuf, size_al,"debug stats start:\n\n");
+	spin_lock_bh(&nss_connmgr_ipv6.lock);
+
+	for (i = 0; (i < NSS_CONNMGR_IPV6_MAX_STR_LENGTH); i++) {
+		stats_shadow[i] = nss_connmgr_ipv6.debug_stats[i];
+	}
+
+	spin_unlock_bh(&nss_connmgr_ipv6.lock);
+
+	for (i = 0; (i < NSS_CONNMGR_IPV6_DEBUG_STATS_MAX); i++) {
+		size_wr += scnprintf(lbuf + size_wr, size_al - size_wr,
+					"%s = %u\n", nss_connmgr_ipv6_debug_stats_str[i], stats_shadow[i]);
+	}
+
+	size_wr += scnprintf(lbuf + size_wr, size_al - size_wr,"\ndebug stats end\n\n");
+	bytes_read = simple_read_from_buffer(ubuf, sz, ppos, lbuf, strlen(lbuf));
+	kfree(lbuf);
+
+	return bytes_read;
+}
+
+/*
+ * nss_connmgr_ipv6_clear_stats
+ *      Clear ipv6 stats
+ */
+static ssize_t nss_connmgr_ipv6_clear_stats(struct file *fp, const char __user *ubuf, size_t count , loff_t *ppos)
+{
+	int32_t i,j;
+
+	spin_lock_bh(&nss_connmgr_ipv6.lock);
+
+	for (i = 0; (i < NSS_CONNMGR_IPV6_CONN_MAX); i++) {
+		for (j = 0; (j < NSS_CONNMGR_IPV6_STATS_MAX); j++) {
+			nss_connmgr_ipv6.connection[i].stats[j] = 0;
+		}
+	}
+
+	for (i = 0; (i < NSS_CONNMGR_IPV6_DEBUG_STATS_MAX); i++) {
+		nss_connmgr_ipv6.debug_stats[i] = 0;
+	}
+
+	spin_unlock_bh(&nss_connmgr_ipv6.lock);
+
+	return count;
+}
+
+/*
+ * nss_connmgr_ipv6_module_get()
+ *	Take a reference to the module
+ */
+void nss_connmgr_ipv6_module_get(void)
+{
+	try_module_get(THIS_MODULE);
+}
+EXPORT_SYMBOL(nss_connmgr_ipv6_module_get);
+
+/*
+ * nss_connmgr_ipv6_module_put()
+ *	Release a reference to the module
+ */
+void nss_connmgr_ipv6_module_put(void)
+{
+	module_put(THIS_MODULE);
+}
+EXPORT_SYMBOL(nss_connmgr_ipv6_module_put);
+
+/*
+ * nss_connmgr_ipv6_init()
+ */
+static int __init nss_connmgr_ipv6_init(void)
+{
+	struct dentry *dent, *dfile;
+
+	NSS_CONNMGR_DEBUG_INFO("NSS Connection Manager Module init\n");
+
+	/*
+	 * Initialise our global database lock
+	 */
+	spin_lock_init(&nss_connmgr_ipv6.lock);
+
+	/*
+	 * Create a thread to handle the start/stop.
+	 * NOTE: We use a thread as some things we need to do cannot be done in this context
+	 */
+	nss_connmgr_ipv6.terminate = 0;
+	nss_connmgr_ipv6.thread = kthread_create(nss_connmgr_ipv6_thread_fn, NULL, "%s", "nss_connmgr_ipv6_thread");
+	if (!nss_connmgr_ipv6.thread) {
+		return -EINVAL;
+	}
+	wake_up_process(nss_connmgr_ipv6.thread);
+
+	/*
+	 * Create debugfs files for stats
+	 */
+	dent = debugfs_create_dir("qca-nss-connmgr-ipv6", NULL);
+
+	if (unlikely(dent == NULL)) {
+
+		/*
+		 * Non-availability of debugfs dir is not catastrophe.
+		 * Print a message and gracefully return
+		 */
+		NSS_CONNMGR_DEBUG_WARN("Failed to create nss_connmgr_ipv6 dir in debugfs \n");
+		return 0;
+	}
+
+	nss_connmgr_ipv6.dent = dent;
+	dfile = debugfs_create_file("connection_stats", S_IRUGO , dent, &nss_connmgr_ipv6, &nss_connmgr_ipv6_show_stats_ops);
+
+	if (unlikely(dfile == NULL))
+	{
+		NSS_CONNMGR_DEBUG_WARN("Failed to create nss_connmgr_ipv6/connection_stats in debugfs \n");
+		debugfs_remove(dent);
+	}
+
+	dfile = debugfs_create_file("debug_stats", S_IRUGO , dent, &nss_connmgr_ipv6, &nss_connmgr_ipv6_show_debug_stats_ops);
+
+	if (unlikely(dfile == NULL))
+	{
+		NSS_CONNMGR_DEBUG_WARN("Failed to create nss_connmgr_ipv6/show_stats in debugfs \n");
+		debugfs_remove(dent);
+	}
+
+	dfile = debugfs_create_file("clear_stats", S_IRUGO | S_IWUSR , dent, &nss_connmgr_ipv6, &nss_connmgr_ipv6_clear_stats_ops);
+
+	if (unlikely(dfile == NULL))
+	{
+		NSS_CONNMGR_DEBUG_WARN("Failed to create nss_connmgr_ipv6/clear_stats in debugfs \n");
+		debugfs_remove(dent);
+	}
+
+	return 0;
+}
+
+/*
+ * nss_connmgr_ipv6_exit()
+ */
+static void __exit nss_connmgr_ipv6_exit(void)
+{
+	/*
+	 * Remove debugfs tree
+	 */
+	if (likely(nss_connmgr_ipv6.dent != NULL)) {
+		debugfs_remove_recursive(nss_connmgr_ipv6.dent);
+	}
+
+	NSS_CONNMGR_DEBUG_INFO("NSS Connection Manager Module exit\n");
+}
+
+module_init(nss_connmgr_ipv6_init);
+module_exit(nss_connmgr_ipv6_exit);
+
+MODULE_AUTHOR("Qualcomm Atheros Inc");
+MODULE_DESCRIPTION("NSS ipv6 Connection manager");
+MODULE_LICENSE("Dual BSD/GPL");
+