[qca-nss-clients] IPv6 Auto-rule insertion support.

Added support for inserting flow rules automatically to offload
IPsec-over-IPv6 flows.

Change-Id: Ibc3fdb1f5d8638c39ee063f219888ca49f4c12ba
Signed-off-by: mandrw <mandrw@codeaurora.org>
diff --git a/ipsecmgr/nss_ipsecmgr_flow.c b/ipsecmgr/nss_ipsecmgr_flow.c
index 7f1dc79..5ce24e2 100644
--- a/ipsecmgr/nss_ipsecmgr_flow.c
+++ b/ipsecmgr/nss_ipsecmgr_flow.c
@@ -92,6 +92,7 @@
 	struct nss_ipsecmgr_flow_entry *flow;
 	struct nss_ipsec_rule_sel local_sel;
 	struct nss_ipsec_rule_sel *flow_sel;
+	struct nss_ipsec_msg nss_nim;
 
 	flow = container_of(ref, struct nss_ipsecmgr_flow_entry, ref);
 	flow_sel = &flow->nim.msg.push.sel;
@@ -110,7 +111,12 @@
 		memcpy(flow_sel, &local_sel, sizeof(struct nss_ipsec_rule_sel));
 	}
 
-	if (nss_ipsec_tx_msg(priv->nss_ctx, &flow->nim) != NSS_TX_SUCCESS) {
+	/*
+	 * Convert the message to NSS format
+	 */
+	nss_ipsecmgr_copy_nim(&flow->nim, &nss_nim);
+
+	if (nss_ipsec_tx_msg(priv->nss_ctx, &nss_nim) != NSS_TX_SUCCESS) {
 		/*
 		 * XXX: Stop the TX queue and add this "entry"
 		 * to pending queue
@@ -127,13 +133,19 @@
 static void nss_ipsecmgr_flow_free(struct nss_ipsecmgr_priv *priv, struct nss_ipsecmgr_ref *ref)
 {
 	struct nss_ipsecmgr_flow_entry *flow = container_of(ref, struct nss_ipsecmgr_flow_entry, ref);
+	struct nss_ipsec_msg nss_nim;
 
 	/*
 	 * update the common message structure
 	 */
 	flow->nim.cm.type = NSS_IPSEC_MSG_TYPE_DEL_RULE;
 
-	if (nss_ipsec_tx_msg(priv->nss_ctx, &flow->nim) != NSS_TX_SUCCESS) {
+	/*
+	 * Convert the message to NSS format
+	 */
+	nss_ipsecmgr_copy_nim(&flow->nim, &nss_nim);
+
+	if (nss_ipsec_tx_msg(priv->nss_ctx, &nss_nim) != NSS_TX_SUCCESS) {
 		/*
 		 * XXX: add this "entry" to pending queue
 		 */
@@ -153,8 +165,6 @@
 /*
  * nss_ipsecmgr_flow_stats_resp()
  * 	response for the flow message
- *
- * Note: we don't have anything to process for flow responses as of now
  */
 static void nss_ipsecmgr_flow_stats_resp(void *app_data, struct nss_ipsec_msg *nim)
 {
@@ -179,6 +189,14 @@
 	sel = &nim->msg.flow_stats.sel;
 
 	/*
+	 * NSS uses a hybrid endian order for v6 addresses. Need to convert to host
+	 */
+	if (sel->ip_ver == NSS_IPSEC_IPVER_6) {
+		nss_ipsecmgr_v6addr_swap(sel->dst_addr, sel->dst_addr);
+		nss_ipsecmgr_v6addr_swap(sel->src_addr, sel->src_addr);
+	}
+
+	/*
 	 * prepare key from selector
 	 */
 	switch (interface) {
@@ -226,11 +244,12 @@
 	struct dentry *parent = dget_parent(fp->f_dentry);
 	uint32_t tunnel_id = (uint32_t)fp->private_data;
 	struct nss_ipsecmgr_flow_entry *flow;
-	struct nss_ipsec_rule_sel *flow_sel;
+	struct nss_ipsec_rule_sel flow_sel;
 	struct nss_ipsecmgr_priv *priv;
 	struct nss_ipsecmgr_ref *ref;
 	struct nss_ipsecmgr_key key;
 	struct nss_ipsec_msg nim;
+	uint32_t pkts_processed;
 	struct net_device *dev;
 	uint16_t interface;
 	uint32_t addr[4];
@@ -279,6 +298,14 @@
 
 	read_unlock_bh(&priv->lock);
 
+	if (nim.msg.flow_stats.sel.ip_ver == NSS_IPSEC_IPVER_6) {
+		/*
+		 * change the IPv6 address to NSS order before sending
+		 */
+		nss_ipsecmgr_v6addr_swap(nim.msg.flow_stats.sel.src_addr, nim.msg.flow_stats.sel.src_addr);
+		nss_ipsecmgr_v6addr_swap(nim.msg.flow_stats.sel.dst_addr, nim.msg.flow_stats.sel.dst_addr);
+	}
+
 	/*
 	 * send stats message to nss
 	 */
@@ -296,12 +323,6 @@
 		goto done;
 	}
 
-	local = vzalloc(NSS_IPSECMGR_MAX_BUF_SZ);
-	if (!local) {
-		nss_ipsecmgr_error("unable to allocate local buffer for tunnel-id: %d\n", tunnel_id);
-		goto done;
-	}
-
 	/*
 	 * After wait_for_completion, confirm if flow still exist
 	 */
@@ -309,13 +330,21 @@
 	ref = nss_ipsecmgr_flow_lookup(priv, &key);
 	if (!ref) {
 		read_unlock_bh(&priv->lock);
-		vfree(local);
 		nss_ipsecmgr_error("flow not found tunnel-id: %d\n", tunnel_id);
 		goto done;
 	}
 
 	flow = container_of(ref, struct nss_ipsecmgr_flow_entry, ref);
-	flow_sel = &flow->nim.msg.push.sel;
+	memcpy(&flow_sel, &flow->nim.msg.push.sel, sizeof(flow_sel));
+	pkts_processed = flow->pkts_processed;
+
+	read_unlock_bh(&priv->lock);
+
+	local = vzalloc(NSS_IPSECMGR_MAX_BUF_SZ);
+	if (!local) {
+		nss_ipsecmgr_error("unable to allocate local buffer for tunnel-id: %d\n", tunnel_id);
+		goto done;
+	}
 
 	/*
 	 * IPv4 Generel info
@@ -335,27 +364,26 @@
 
 	len = 0;
 	len += snprintf(local + len, NSS_IPSECMGR_MAX_BUF_SZ - len, "type:%s\n", type);
-	switch (flow_sel->ip_ver) {
+	switch (flow_sel.ip_ver) {
 	case NSS_IPSEC_IPVER_4:
-		len += snprintf(local + len, NSS_IPSECMGR_MAX_BUF_SZ - len, "dst_ip: %pI4h\n", &flow_sel->dst_addr[0]);
-		len += snprintf(local + len, NSS_IPSECMGR_MAX_BUF_SZ - len, "src_ip: %pI4h\n", &flow_sel->src_addr[0]);
+		len += snprintf(local + len, NSS_IPSECMGR_MAX_BUF_SZ - len, "dst_ip: %pI4h\n", flow_sel.dst_addr);
+		len += snprintf(local + len, NSS_IPSECMGR_MAX_BUF_SZ - len, "src_ip: %pI4h\n", flow_sel.src_addr);
 		break;
 
 	case NSS_IPSEC_IPVER_6:
-		len += snprintf(local + len, NSS_IPSECMGR_MAX_BUF_SZ - len, "dst_ip: %pI6c\n", nss_ipsecmgr_v6addr_ntohl(flow_sel->dst_addr, addr));
-		len += snprintf(local + len, NSS_IPSECMGR_MAX_BUF_SZ - len, "src_ip: %pI6c\n", nss_ipsecmgr_v6addr_ntohl(flow_sel->src_addr, addr));
+		len += snprintf(local + len, NSS_IPSECMGR_MAX_BUF_SZ - len, "dst_ip: %pI6c\n", nss_ipsecmgr_v6addr_ntoh(flow_sel.dst_addr, addr));
+		len += snprintf(local + len, NSS_IPSECMGR_MAX_BUF_SZ - len, "src_ip: %pI6c\n", nss_ipsecmgr_v6addr_ntoh(flow_sel.src_addr, addr));
 		break;
 
 	}
 
-	len += snprintf(local + len, NSS_IPSECMGR_MAX_BUF_SZ - len, "proto: %d\n", flow_sel->proto_next_hdr);
+	len += snprintf(local + len, NSS_IPSECMGR_MAX_BUF_SZ - len, "proto: %d\n", flow_sel.proto_next_hdr);
 
 	/*
 	 * packet stats
 	 */
-	len += snprintf(local + len, NSS_IPSECMGR_MAX_BUF_SZ - len, "processed: %d\n", flow->pkts_processed);
+	len += snprintf(local + len, NSS_IPSECMGR_MAX_BUF_SZ - len, "processed: %d\n", pkts_processed);
 
-	read_unlock_bh(&priv->lock);
 
 	ret = simple_read_from_buffer(ubuf, sz, ppos, local, len + 1);
 	vfree(local);
@@ -774,7 +802,7 @@
 		/*
 		 * copy nim data from subnet entry
 		 */
-		nss_ipsecmgr_copy_v4_subnet(&nim, subnet_ref);
+		nss_ipsecmgr_copy_subnet(&nim, subnet_ref);
 
 		/*
 		 * if, the same flow was added in between then flow alloc will return the
@@ -795,7 +823,6 @@
 		nss_ipsecmgr_ref_update(priv, flow_ref, &nim);
 
 		write_unlock(&priv->lock);
-
 		break;
 
 	case htons(ETH_P_IPV6):
@@ -814,10 +841,51 @@
 		/*
 		 * if flow is found then proceed with the TX
 		 */
-		if (!flow_ref) {
+		if (flow_ref) {
+			return true;
+		}
+
+		/*
+		 * flow table miss results in lookup in the subnet table. If,
+		 * a match is found then a rule is inserted in NSS for encapsulating
+		 * this flow.
+		 */
+		nss_ipsecmgr_v6_subnet_sel2key(sel, &subnet_key);
+
+		/*
+		 * write lock as it can update the flow database
+		 */
+		write_lock(&priv->lock);
+
+		subnet_ref = nss_ipsecmgr_v6_subnet_match(priv, &subnet_key);
+		if (!subnet_ref) {
+			write_unlock(&priv->lock);
 			return false;
 		}
 
+		/*
+		 * copy nim data from subnet entry
+		 */
+		nss_ipsecmgr_copy_subnet(&nim, subnet_ref);
+
+		/*
+		 * if, the same flow was added in between then flow alloc will return the
+		 * same flow. The only side affect of this will be NSS getting duplicate
+		 * add requests and thus rejecting one of them
+		 */
+		flow_ref = nss_ipsecmgr_flow_alloc(priv, &flow_key);
+		if (!flow_ref) {
+			write_unlock(&priv->lock);
+			return false;
+		}
+
+		/*
+		 * add reference to subnet and trigger an update
+		 */
+		nss_ipsecmgr_ref_add(flow_ref, subnet_ref);
+		nss_ipsecmgr_ref_update(priv, flow_ref, &nim);
+
+		write_unlock(&priv->lock);
 		break;
 
 	default:
diff --git a/ipsecmgr/nss_ipsecmgr_priv.h b/ipsecmgr/nss_ipsecmgr_priv.h
index b07ea43..6f0c503 100644
--- a/ipsecmgr/nss_ipsecmgr_priv.h
+++ b/ipsecmgr/nss_ipsecmgr_priv.h
@@ -76,7 +76,7 @@
 #error "NSS_IPSECMGR_MAX_SUBNET is not a power of 2"
 #endif
 
-#define NSS_IPSECMGR_MAX_NETMASK 32 /* Max ipv4 subnets */
+#define NSS_IPSECMGR_MAX_NETMASK 64 /* Max subnets */
 #if (~(NSS_IPSECMGR_MAX_NETMASK - 1) & (NSS_IPSECMGR_MAX_NETMASK >> 1))
 #error "NSS_IPSECMGR_MAX_NETMASK is not a power of 2"
 #endif
@@ -104,6 +104,7 @@
 	NSS_IPSECMGR_KEY_LEN_IPV4_ENCAP_FLOW = 3,	/* 12 bytes */
 	NSS_IPSECMGR_KEY_LEN_IPV4_DECAP_FLOW = 4,	/* 16 bytes */
 	NSS_IPSECMGR_KEY_LEN_IPV6_SA = 10,		/* 40 bytes */
+	NSS_IPSECMGR_KEY_LEN_IPV6_SUBNET = 3,		/* 12 bytes */
 	NSS_IPSECMGR_KEY_LEN_IPV6_ENCAP_FLOW = 9,	/* 36 bytes */
 	NSS_IPSECMGR_KEY_LEN_IPV6_DECAP_FLOW = 10,	/* 40 bytes */
 	NSS_IPSECMGR_KEY_LEN_MAX = NSS_IPSECMGR_MAX_KEY_WORDS
@@ -219,8 +220,8 @@
  * IPsec netmask entry
  */
 struct nss_ipsecmgr_netmask_entry {
+	uint64_t mask_bits;					/* no. of bits in netmask */
 	uint32_t count;						/* no. of subnets entries */
-	uint32_t mask_bits;					/* no. of bits in netmask */
 	struct list_head subnets[NSS_IPSECMGR_MAX_SUBNET];	/* subnet database */
 };
 
@@ -601,7 +602,6 @@
 	*data = v;
 	*mask = NSS_IPSECMGR_GENMASK(31, 0);
 }
-
 /*
  * nss_ipsecmgr_key_clear_32()
  * 	clear 32-bit mask from the specified position
@@ -615,29 +615,51 @@
 }
 
 /*
- * nss_ipsecmgr_key_read()
- * 	read value from the specified position using mask
+ * nss_ipsecmgr_key_read_n()
+ * 	read n nnumber of values from the specified position using mask
  */
-static inline void nss_ipsecmgr_key_read(struct nss_ipsecmgr_key *key, uint32_t *v, uint32_t *m, enum nss_ipsecmgr_key_pos p)
+static inline void nss_ipsecmgr_key_read(struct nss_ipsecmgr_key *key, uint32_t *v, uint32_t *m, enum nss_ipsecmgr_key_pos p, uint32_t n)
 {
+	uint32_t *k_data, *k_mask;
 	uint16_t idx;
+	int delta;
 
 	idx = BIT_WORD(p) % NSS_IPSECMGR_MAX_KEY_WORDS;
-	*v = key->data[idx];
-	*m = key->mask[idx];
+	delta = NSS_IPSECMGR_MAX_KEY_WORDS - idx;
+
+	n = n > delta ? delta : n;
+
+	k_data = &key->data[idx];
+	k_mask = &key->mask[idx];
+
+	for (; n--;) {
+		*v++ = *k_data++;
+		*m++ = *k_mask++;
+	}
 }
 
 /*
  * nss_ipsecmgr_key_write()
  * 	write value from the specified position using mask
  */
-static inline void nss_ipsecmgr_key_write(struct nss_ipsecmgr_key *key, uint32_t v, uint32_t m, enum nss_ipsecmgr_key_pos p)
+static inline void nss_ipsecmgr_key_write(struct nss_ipsecmgr_key *key, uint32_t *v, uint32_t *m, enum nss_ipsecmgr_key_pos p, uint32_t n)
 {
+	uint32_t *k_data, *k_mask;
 	uint16_t idx;
+	int delta;
 
 	idx = BIT_WORD(p) % NSS_IPSECMGR_MAX_KEY_WORDS;
-	key->data[idx] = v;
-	key->mask[idx] = m;
+	delta = NSS_IPSECMGR_MAX_KEY_WORDS - idx;
+
+	n = n > delta ? delta : n;
+
+	k_data = &key->data[idx];
+	k_mask = &key->mask[idx];
+
+	for (; n--;) {
+		*k_data++ = *v++;
+		*k_mask++ = *m++;
+	}
 }
 
 /*
@@ -653,6 +675,35 @@
 }
 
 /*
+ * nss_ipsecmgr_read_key_mask32()
+ * 	read a 32 bit mask starting from position
+ */
+static inline uint32_t nss_ipsecmgr_key_read_mask32(struct nss_ipsecmgr_key *key, enum nss_ipsecmgr_key_pos p)
+{
+	uint16_t idx;
+
+	idx = BIT_WORD(p) % NSS_IPSECMGR_MAX_KEY_WORDS;
+
+	return key->mask[idx];
+}
+
+/*
+ * nss_ipsecmgr_read_key_mask64()
+ * 	read a 64 bit mask starting from position
+ */
+static inline uint64_t nss_ipsecmgr_key_read_mask64(struct nss_ipsecmgr_key *key, enum nss_ipsecmgr_key_pos p)
+{
+	uint64_t *mask;
+	uint16_t idx;
+
+	idx = BIT_WORD(p) % NSS_IPSECMGR_MAX_KEY_WORDS;
+
+	mask = (uint64_t *)&key->mask[idx];
+
+	return *mask;
+}
+
+/*
  * nss_ipsecmgr_key_lshift_mask()
  * 	left shift mask by an amount 's'
  */
@@ -665,6 +716,21 @@
 }
 
 /*
+ * nss_ipsecmgr_key_lshift_mask64()
+ * 	left shift mask by an amount 's'
+ */
+static inline void nss_ipsecmgr_key_lshift_mask64(struct nss_ipsecmgr_key *key, uint32_t s, enum nss_ipsecmgr_key_pos p)
+{
+	uint64_t *mask;
+	uint16_t idx;
+
+	idx = BIT_WORD(p) % NSS_IPSECMGR_MAX_KEY_WORDS;
+
+	mask = (uint64_t *)&key->mask[idx];
+	*mask <<= s;
+}
+
+/*
  * nss_ipsecmgr_key_cmp_data()
  * 	compare source key with destination key
  *
@@ -787,7 +853,7 @@
 	char *tmp = str;
 	uint8_t *hash;
 
-	nss_ipsecmgr_key_read(key, &data, &mask, pos);
+	nss_ipsecmgr_key_read(key, &data, &mask, pos, 1);
 	hash = (uint8_t *)&mask;
 
 	str = hex_byte_pack(str, hash[0]);
@@ -875,21 +941,81 @@
 }
 
 /*
- * nss_ipsecmgr_v6addr_ntohl()
- * 	convert the v6 address to
+ * nss_ipsecmgr_v6addr_swap()
+ * 	Swap the words in the array.
  */
-static inline uint32_t *nss_ipsecmgr_v6addr_ntohl(uint32_t src[], uint32_t dst[])
+static uint32_t *nss_ipsecmgr_v6addr_swap(uint32_t *src, uint32_t *dst)
 {
-	int i = 4;
+	uint32_t temp[4];
 
-	while (i--) {
-		dst[i] = ntohl(src[i]);
+	if (src == dst) {
+		memcpy(temp, src, sizeof(temp));
+		src = temp;
 	}
 
+	dst[3] = src[0];
+	dst[2] = src[1];
+	dst[1] = src[2];
+	dst[0] = src[3];
+
 	return dst;
 }
 
 /*
+ * nss_ipsecmgr_v6addr_ntoh()
+ * 	convert the v6 address form network to host order.
+ */
+static inline uint32_t *nss_ipsecmgr_v6addr_ntoh(uint32_t *src, uint32_t *dst)
+{
+	uint32_t *dst_addr = nss_ipsecmgr_v6addr_swap(src, dst);
+
+	dst_addr[0] = ntohl(dst_addr[0]);
+	dst_addr[1] = ntohl(dst_addr[1]);
+	dst_addr[2] = ntohl(dst_addr[2]);
+	dst_addr[3] = ntohl(dst_addr[3]);
+
+	return dst_addr;
+}
+
+/*
+ * nss_ipsecmgr_v6addr_hton()
+ * 	convert the v6 address to host to network order.
+ */
+static inline uint32_t *nss_ipsecmgr_v6addr_hton(uint32_t *src, uint32_t *dst)
+{
+	uint32_t *dst_addr = nss_ipsecmgr_v6addr_swap(src, dst);
+
+	dst_addr[0] = htonl(dst_addr[0]);
+	dst_addr[1] = htonl(dst_addr[1]);
+	dst_addr[2] = htonl(dst_addr[2]);
+	dst_addr[3] = htonl(dst_addr[3]);
+
+	return dst_addr;
+}
+
+/*
+ * nss_ipsecmgr_copy_nim()
+ * 	copy nim from source to destination, also swap the v6 addresses if any
+ */
+static inline void nss_ipsecmgr_copy_nim(struct nss_ipsec_msg *src_nim, struct nss_ipsec_msg *dst_nim)
+{
+	struct nss_ipsec_rule *src_push = &src_nim->msg.push;
+	struct nss_ipsec_rule *dst_push = &dst_nim->msg.push;
+
+	memcpy(dst_nim, src_nim, sizeof(struct nss_ipsec_msg));
+
+	if (src_push->sel.ip_ver == NSS_IPSEC_IPVER_6) {
+		nss_ipsecmgr_v6addr_swap(src_push->sel.dst_addr, dst_push->sel.dst_addr);
+		nss_ipsecmgr_v6addr_swap(src_push->sel.src_addr, dst_push->sel.src_addr);
+	}
+
+	if (src_push->oip.ip_ver == NSS_IPSEC_IPVER_6) {
+		nss_ipsecmgr_v6addr_swap(src_push->oip.dst_addr, dst_push->oip.dst_addr);
+		nss_ipsecmgr_v6addr_swap(src_push->oip.src_addr, dst_push->oip.src_addr);
+	}
+}
+
+/*
  * nss_ipsecmgr_v6_hdr2sel()
  * 	convert v6_hdr to message sel
  */
@@ -908,8 +1034,8 @@
 		nexthdr = frag->nexthdr;
 	}
 
-	nss_ipsecmgr_v6addr_ntohl(iph->daddr.s6_addr32, sel->dst_addr);
-	nss_ipsecmgr_v6addr_ntohl(iph->saddr.s6_addr32, sel->src_addr);
+	nss_ipsecmgr_v6addr_ntoh(iph->daddr.s6_addr32, sel->dst_addr);
+	nss_ipsecmgr_v6addr_ntoh(iph->saddr.s6_addr32, sel->src_addr);
 
 	sel->proto_next_hdr = nexthdr;
 	sel->ip_ver = NSS_IPSEC_IPVER_6;
@@ -968,9 +1094,11 @@
 /*
  * Subnet API(s)
  */
-void nss_ipsecmgr_copy_v4_subnet(struct nss_ipsec_msg *nim, struct nss_ipsecmgr_ref *subnet_ref);
+void nss_ipsecmgr_copy_subnet(struct nss_ipsec_msg *nim, struct nss_ipsecmgr_ref *subnet_ref);
 void nss_ipsecmgr_v4_subnet2key(struct nss_ipsecmgr_encap_v4_subnet *subnet, struct nss_ipsecmgr_key *key);
+void nss_ipsecmgr_v6_subnet2key(struct nss_ipsecmgr_encap_v6_subnet *subnet, struct nss_ipsecmgr_key *key);
 void nss_ipsecmgr_v4_subnet_sel2key(struct nss_ipsec_rule_sel *sel, struct nss_ipsecmgr_key *key);
+void nss_ipsecmgr_v6_subnet_sel2key(struct nss_ipsec_rule_sel *sel, struct nss_ipsecmgr_key *key);
 
 /*
  * Subnet alloc/lookup API(s)
@@ -978,6 +1106,7 @@
 struct nss_ipsecmgr_ref *nss_ipsecmgr_subnet_alloc(struct nss_ipsecmgr_priv *priv, struct nss_ipsecmgr_key *key);
 struct nss_ipsecmgr_ref *nss_ipsecmgr_subnet_lookup(struct nss_ipsecmgr_priv *priv, struct nss_ipsecmgr_key *key);
 struct nss_ipsecmgr_ref *nss_ipsecmgr_v4_subnet_match(struct nss_ipsecmgr_priv *priv, struct nss_ipsecmgr_key *sel);
+struct nss_ipsecmgr_ref *nss_ipsecmgr_v6_subnet_match(struct nss_ipsecmgr_priv *priv, struct nss_ipsecmgr_key *sel);
 
 /*
  * SA API(s)
diff --git a/ipsecmgr/nss_ipsecmgr_sa.c b/ipsecmgr/nss_ipsecmgr_sa.c
index a7993c8..6d8d26a 100644
--- a/ipsecmgr/nss_ipsecmgr_sa.c
+++ b/ipsecmgr/nss_ipsecmgr_sa.c
@@ -144,8 +144,8 @@
 		break;
 
 	case NSS_IPSEC_IPVER_6:
-		len += snprintf(local + len, NSS_IPSECMGR_MAX_BUF_SZ - len, "dst_ip: %pI6c\n", nss_ipsecmgr_v6addr_ntohl(oip->dst_addr, addr));
-		len += snprintf(local + len, NSS_IPSECMGR_MAX_BUF_SZ - len, "src_ip: %pI6c\n", nss_ipsecmgr_v6addr_ntohl(oip->src_addr, addr));
+		len += snprintf(local + len, NSS_IPSECMGR_MAX_BUF_SZ - len, "dst_ip: %pI6c\n", nss_ipsecmgr_v6addr_hton(oip->dst_addr, addr));
+		len += snprintf(local + len, NSS_IPSECMGR_MAX_BUF_SZ - len, "src_ip: %pI6c\n", nss_ipsecmgr_v6addr_hton(oip->src_addr, addr));
 		break;
 
 	}
@@ -513,6 +513,9 @@
 		nss_ipsecmgr_key_write_8(key, 6 /* v6 */, NSS_IPSECMGR_KEY_POS_IP_VER);
 		nss_ipsecmgr_key_write_8(key, IPPROTO_ESP, NSS_IPSECMGR_KEY_POS_IP_PROTO);
 
+		nss_ipsecmgr_v6addr_swap(sel->dst_addr, sel->dst_addr);
+		nss_ipsecmgr_v6addr_swap(sel->src_addr, sel->src_addr);
+
 		for (i  = 0; i < 4; i++) {
 			nss_ipsecmgr_key_write_32(key, sel->dst_addr[i], NSS_IPSECMGR_KEY_POS_IPV6_DST + (i * 32));
 			nss_ipsecmgr_key_write_32(key, sel->src_addr[i], NSS_IPSECMGR_KEY_POS_IPV6_SRC + (i * 32));
@@ -732,6 +735,7 @@
 		info.child_alloc = nss_ipsecmgr_subnet_alloc;
 		info.child_lookup = nss_ipsecmgr_subnet_lookup;
 		break;
+
 	case NSS_IPSECMGR_FLOW_TYPE_V6_TUPLE:
 
 		nss_ipsecmgr_copy_encap_v6_flow(&info.nim, &flow->data.v6_tuple);
@@ -745,6 +749,17 @@
 		info.child_lookup = nss_ipsecmgr_flow_lookup;
 		break;
 
+	case NSS_IPSECMGR_FLOW_TYPE_V6_SUBNET:
+
+		nss_ipsecmgr_copy_v6_sa(&info.nim, &sa->data.v6);
+		nss_ipsecmgr_copy_sa_data(&info.nim, data);
+
+		nss_ipsecmgr_v6_subnet2key(&flow->data.v6_subnet, &info.child_key);
+		nss_ipsecmgr_v6_sa2key(&sa->data.v6, &info.sa_key);
+
+		info.child_alloc = nss_ipsecmgr_subnet_alloc;
+		info.child_lookup = nss_ipsecmgr_subnet_lookup;
+		break;
 
 	default:
 		nss_ipsecmgr_warn("%p:unknown flow type(%d)\n", tun, flow->type);
@@ -814,6 +829,17 @@
 		info.child_lookup = nss_ipsecmgr_flow_lookup;
 		break;
 
+	case NSS_IPSECMGR_FLOW_TYPE_V6_SUBNET:
+
+		nss_ipsecmgr_copy_v6_sa(&info.nim, &sa->data.v6);
+
+		nss_ipsecmgr_v6_subnet2key(&flow->data.v6_subnet, &info.child_key);
+		nss_ipsecmgr_v6_sa2key(&sa->data.v6, &info.sa_key);
+
+		info.child_alloc = nss_ipsecmgr_subnet_alloc;
+		info.child_lookup = nss_ipsecmgr_subnet_lookup;
+		break;
+
 	default:
 		nss_ipsecmgr_warn("%p:unknown flow type(%d)\n", tun, flow->type);
 		return false;
diff --git a/ipsecmgr/nss_ipsecmgr_subnet.c b/ipsecmgr/nss_ipsecmgr_subnet.c
index a94ba7c..5479cf1 100644
--- a/ipsecmgr/nss_ipsecmgr_subnet.c
+++ b/ipsecmgr/nss_ipsecmgr_subnet.c
@@ -60,10 +60,14 @@
  */
 static inline bool nss_ipsecmgr_netmask_is_default(struct nss_ipsecmgr_key *key)
 {
-	uint32_t data, mask;
+	uint8_t ip_ver;
 
-	nss_ipsecmgr_key_read(key, &data, &mask, NSS_IPSECMGR_KEY_POS_IPV4_DST);
-	return !mask;
+	ip_ver = nss_ipsecmgr_key_read_8(key, NSS_IPSECMGR_KEY_POS_IP_VER);
+	if (likely(ip_ver == 4)) {
+		return !nss_ipsecmgr_key_read_mask32(key, NSS_IPSECMGR_KEY_POS_IPV4_DST);
+	}
+
+	return !nss_ipsecmgr_key_read_mask64(key, NSS_IPSECMGR_KEY_POS_IPV6_DST);
 }
 
 /*
@@ -124,17 +128,26 @@
 	return NULL;
 }
 
-
+/*
+ * nss_ipsecmgr_netmask2idx()
+ * 	Get the index of the netmask array from key.
+ */
 static inline uint32_t nss_ipsecmgr_netmask2idx(struct nss_ipsecmgr_key *key)
 {
-	uint32_t mask, data;
+	uint32_t mask32;
+	uint64_t mask64;
+	uint8_t ip_ver;
 
-	nss_ipsecmgr_key_read(key, &data, &mask, NSS_IPSECMGR_KEY_POS_IPV4_DST);
+	ip_ver = nss_ipsecmgr_key_read_8(key, NSS_IPSECMGR_KEY_POS_IP_VER);
+	if (likely(ip_ver == 4)) {
+		mask32  = nss_ipsecmgr_key_read_mask32(key, NSS_IPSECMGR_KEY_POS_IPV4_DST);
+		return ffs(mask32) - 1;
+	}
 
-	/*
-	 * for zero mask this will return 0xFFFF_FFFF
-	 */
-	return ffs(mask) - 1;
+	BUG_ON(ip_ver != 6);
+
+	mask64 = nss_ipsecmgr_key_read_mask64(key, NSS_IPSECMGR_KEY_POS_IPV6_DST);
+	return mask64 ? __ffs64(mask64) : NSS_IPSECMGR_MAX_NETMASK;
 }
 
 /*
@@ -189,12 +202,13 @@
 static ssize_t nss_ipsecmgr_subnet_stats_read(struct file *fp, char __user *ubuf, size_t sz, loff_t *ppos)
 {
 	struct dentry *parent = dget_parent(fp->f_dentry);
-	struct nss_ipsecmgr_subnet_entry *subnet;
+	struct nss_ipsecmgr_subnet_entry *subnet_entry;
 	struct nss_ipsecmgr_priv *priv;
 	struct nss_ipsecmgr_ref *ref;
+	uint32_t subnet[4], mask[4];
 	struct net_device *dev;
-	uint32_t dst, m_dst;
 	ssize_t ret = 0;
+	uint8_t ip_ver;
 	uint8_t proto;
 	char *local;
 	int len;
@@ -205,6 +219,9 @@
 	}
 	priv = netdev_priv(dev);
 
+	/*
+	 * Take the read lock.
+	 */
 	read_lock(&priv->lock);
 
 	ref = nss_ipsecmgr_subnet_name_lookup(priv, parent->d_name.name);
@@ -214,15 +231,28 @@
 		goto done;
 	}
 
-	subnet = container_of(ref, struct nss_ipsecmgr_subnet_entry, ref);
+	subnet_entry = container_of(ref, struct nss_ipsecmgr_subnet_entry, ref);
 
-	/*
-	 * Extract info from key.
-	 * NOTE: for IPv6 respective positions for dst_ip and netmask should be used.
-	 */
-	nss_ipsecmgr_key_read(&subnet->key, &dst, &m_dst, NSS_IPSECMGR_KEY_POS_IPV4_DST);
-	proto = nss_ipsecmgr_key_read_8(&subnet->key, NSS_IPSECMGR_KEY_POS_IP_PROTO);
+	proto = nss_ipsecmgr_key_read_8(&subnet_entry->key, NSS_IPSECMGR_KEY_POS_IP_PROTO);
+	ip_ver = nss_ipsecmgr_key_read_8(&subnet_entry->key, NSS_IPSECMGR_KEY_POS_IP_VER);
 
+	switch (ip_ver) {
+	case 4:
+		nss_ipsecmgr_key_read(&subnet_entry->key, subnet, mask, NSS_IPSECMGR_KEY_POS_IPV4_DST, 1);
+		break;
+
+	case 6:
+		/*
+		 * The subnet and mask bits from the key are read to the upper words of the array
+		 * later converted to network order for display.
+		 */
+		nss_ipsecmgr_key_read(&subnet_entry->key, &subnet[2], &mask[2], NSS_IPSECMGR_KEY_POS_IPV6_DST, 2);
+
+		nss_ipsecmgr_v6addr_hton(subnet, subnet);
+		nss_ipsecmgr_v6addr_hton(mask, mask);
+		break;
+
+	}
 	read_unlock(&priv->lock);
 
 	local = vzalloc(NSS_IPSECMGR_MAX_BUF_SZ);
@@ -231,12 +261,20 @@
 		goto done;
 	}
 
-	/*
-	 * IPv4 Generel info
-	 */
+
 	len = 0;
-	len += snprintf(local + len, NSS_IPSECMGR_MAX_BUF_SZ - len, "dst_ip: %pI4h\n", &dst);
-	len += snprintf(local + len, NSS_IPSECMGR_MAX_BUF_SZ - len, "dst_mask: %pI4h\n", &m_dst);
+	switch (ip_ver) {
+	case 4:
+		len += snprintf(local + len, NSS_IPSECMGR_MAX_BUF_SZ - len, "dst_ip: %pI4h\n", subnet);
+		len += snprintf(local + len, NSS_IPSECMGR_MAX_BUF_SZ - len, "dst_mask: %pI4h\n", mask);
+		break;
+
+	case 6:
+		len += snprintf(local + len, NSS_IPSECMGR_MAX_BUF_SZ - len, "dst_ip: %pI6c\n", subnet);
+		len += snprintf(local + len, NSS_IPSECMGR_MAX_BUF_SZ - len, "dst_mask: %pI6c\n", mask);
+		break;
+	}
+
 	len += snprintf(local + len, NSS_IPSECMGR_MAX_BUF_SZ - len, "proto: %d\n", proto);
 
 	ret = simple_read_from_buffer(ubuf, sz, ppos, local, len + 1);
@@ -346,10 +384,10 @@
 
 
 /*
- * nss_ipsecmgr_copy_v4_subnet()
- * 	copy v4_subnet nim
+ * nss_ipsecmgr_copy_subnet()
+ * 	copy subnet nim
  */
-void nss_ipsecmgr_copy_v4_subnet(struct nss_ipsec_msg *nim, struct nss_ipsecmgr_ref *ref)
+void nss_ipsecmgr_copy_subnet(struct nss_ipsec_msg *nim, struct nss_ipsecmgr_ref *ref)
 {
 	struct nss_ipsecmgr_subnet_entry *entry;
 	struct nss_ipsec_rule_data *data;
@@ -380,6 +418,28 @@
 }
 
 /*
+ * nss_ipsecmgr_v6_subnet_sel2key()
+ * 	convert subnet selector to key
+ */
+void nss_ipsecmgr_v6_subnet_sel2key(struct nss_ipsec_rule_sel *sel, struct nss_ipsecmgr_key *key)
+{
+	nss_ipsecmgr_key_reset(key);
+
+	nss_ipsecmgr_key_write_8(key, 6 /* ipv6 */, NSS_IPSECMGR_KEY_POS_IP_VER);
+	nss_ipsecmgr_key_write_8(key, sel->proto_next_hdr, NSS_IPSECMGR_KEY_POS_IP_PROTO);
+
+	/*
+	 * IPv6 subnetting uses 64 most significant bits as subnet prefix.
+	 * So we store only 2 most significant worlds to the key.
+	 */
+	nss_ipsecmgr_key_write_32(key, sel->dst_addr[2], NSS_IPSECMGR_KEY_POS_IPV6_DST);
+	nss_ipsecmgr_key_write_32(key, sel->dst_addr[3], NSS_IPSECMGR_KEY_POS_IPV6_DST + 32);
+
+	key->len = NSS_IPSECMGR_KEY_LEN_IPV6_SUBNET;
+}
+
+
+/*
  * nss_ipsecmgr_v4_subnet2key()
  *      convert an v4 subnet into a key
  */
@@ -389,7 +449,7 @@
 
 	nss_ipsecmgr_key_write_8(key, 4 /* ipv4 */, NSS_IPSECMGR_KEY_POS_IP_VER);
 	nss_ipsecmgr_key_write_8(key, (uint8_t)net->protocol, NSS_IPSECMGR_KEY_POS_IP_PROTO);
-	nss_ipsecmgr_key_write(key, net->dst_subnet, net->dst_mask, NSS_IPSECMGR_KEY_POS_IPV4_DST);
+	nss_ipsecmgr_key_write(key, &net->dst_subnet, &net->dst_mask, NSS_IPSECMGR_KEY_POS_IPV4_DST, 1);
 
 	/*
 	 * clear mask if caller specify protocol as any (0xff).
@@ -403,6 +463,22 @@
 }
 
 /*
+ * nss_ipsecmgr_v6_subnet2key()
+ *      convert an v6 subnet into a key
+ */
+void nss_ipsecmgr_v6_subnet2key(struct nss_ipsecmgr_encap_v6_subnet *net, struct nss_ipsecmgr_key *key)
+{
+	nss_ipsecmgr_key_reset(key);
+
+	nss_ipsecmgr_key_write_8(key, 6 /* ipv6 */, NSS_IPSECMGR_KEY_POS_IP_VER);
+	nss_ipsecmgr_key_write_8(key, (uint8_t)net->next_hdr, NSS_IPSECMGR_KEY_POS_IP_PROTO);
+	nss_ipsecmgr_key_write(key, net->dst_subnet, net->dst_mask, NSS_IPSECMGR_KEY_POS_IPV6_DST, 2);
+
+	key->len = NSS_IPSECMGR_KEY_LEN_IPV6_SUBNET;
+}
+
+
+/*
  * nss_ipsecmgr_v4_subnet_match()
  * 	peform a v4 subnet based match in netmask database
  */
@@ -418,7 +494,7 @@
 	/*
 	 * cycle through the bitmap for each subnet
 	 */
-	for_each_set_bit(i, db->bitmap, NSS_IPSECMGR_MAX_NETMASK) {
+	for_each_set_bit(i, db->bitmap, 32) {
 
 		BUG_ON(db->entries[i] == NULL);
 		BUG_ON(db->entries[i]->count == 0);
@@ -450,6 +526,38 @@
 }
 
 /*
+ * nss_ipsecmgr_v6_subnet_match()
+ * 	peform a v6 subnet based match in netmask database
+ */
+struct nss_ipsecmgr_ref *nss_ipsecmgr_v6_subnet_match(struct nss_ipsecmgr_priv *priv, struct nss_ipsecmgr_key *key)
+{
+	struct nss_ipsecmgr_netmask_db *db = &priv->net_db;
+	struct nss_ipsecmgr_key tmp_key;
+	struct nss_ipsecmgr_ref *ref;
+	int i;
+
+	memcpy(&tmp_key, key, sizeof(struct nss_ipsecmgr_key));
+
+	/*
+	 * cycle through the bitmap for each subnet
+	 */
+	for_each_set_bit(i, db->bitmap, NSS_IPSECMGR_MAX_NETMASK) {
+
+		BUG_ON(db->entries[i] == NULL);
+		BUG_ON(db->entries[i]->count == 0);
+
+		nss_ipsecmgr_key_lshift_mask64(&tmp_key, i, NSS_IPSECMGR_KEY_POS_IPV6_DST);
+
+		ref = nss_ipsecmgr_subnet_lookup(priv, &tmp_key);
+		if (ref) {
+			return ref;
+		}
+	}
+
+	return NULL;
+}
+
+/*
  * nss_ipsecmgr_subnet_lookup()
  * 	lookup a subnet entry
  */
diff --git a/netlink/include/nss_nlipv6_if.h b/netlink/include/nss_nlipv6_if.h
index a26c816..6a110a0 100644
--- a/netlink/include/nss_nlipv6_if.h
+++ b/netlink/include/nss_nlipv6_if.h
@@ -26,7 +26,8 @@
  */
 #define NSS_NLIPV6_FAMILY "nss_nlipv6"
 
-#define NSS_NLIPV6_ADDR_BITS (sizeof(uint32_t) * 4)
+#define NSS_NLIPV6_ADDR_BITS (sizeof(uint32_t) * 4 * BITS_PER_BYTE)	/* 128 bits */
+#define NSS_NLIPV6_SUBNET_BITS (sizeof(uint32_t) * 2 * BITS_PER_BYTE)	/* 64 bits */
 
 /**
  * @brief IPv6 rule
@@ -52,4 +53,24 @@
 	nss_nlcmn_init_cmd(&rule->cm, sizeof(struct nss_nlipv6_rule), type);
 }
 
+/**
+ * @brief convert ipv6 address to NSS format
+ *
+ * @param rule[IN] IPv6 address
+ */
+static inline void nss_nlipv6_swap_addr(uint32_t *src, uint32_t *dst)
+{
+	uint32_t temp[4];
+
+	if (src == dst) {
+		memcpy(temp, src, sizeof(temp));
+		src = temp;
+	}
+
+	dst[0] = src[3];
+	dst[1] = src[2];
+	dst[2] = src[1];
+	dst[3] = src[0];
+
+}
 #endif /* __NSS_NLIPV6_IF_H */
diff --git a/netlink/nss_nlipsec.c b/netlink/nss_nlipsec.c
index 0dc439f..dadfc5f 100644
--- a/netlink/nss_nlipsec.c
+++ b/netlink/nss_nlipsec.c
@@ -192,6 +192,7 @@
 {
 	struct nss_ipsecmgr_encap_v4_tuple *v4_tuple;
 	struct nss_ipsecmgr_encap_v4_subnet *v4_subnet;
+	struct nss_ipsecmgr_encap_v6_subnet *v6_subnet;
 	struct nss_ipsecmgr_encap_v6_tuple *v6_tuple;
 	struct nss_ipsecmgr_sa_v4 *v4;
 	struct nss_ipsecmgr_sa_v6 *v6;
@@ -221,12 +222,12 @@
 	case NSS_IPSECMGR_FLOW_TYPE_V6_TUPLE:
 		v6_tuple = &encap_flow->data.v6_tuple;
 
-		if (!bitmap_empty((unsigned long *)v6_tuple->src_ip, NSS_NLIPV6_ADDR_BITS)) {
+		if (bitmap_empty((unsigned long *)v6_tuple->src_ip, NSS_NLIPV6_ADDR_BITS)) {
 			nss_nl_error("Invalid v6 src_ip\n");
 			return -1;
 		}
 
-		if (!bitmap_empty((unsigned long *)v6_tuple->dst_ip, NSS_NLIPV6_ADDR_BITS)) {
+		if (bitmap_empty((unsigned long *)v6_tuple->dst_ip, NSS_NLIPV6_ADDR_BITS)) {
 			nss_nl_error("Invalid v6 dst_ip\n");
 			return -1;
 		}
@@ -238,6 +239,15 @@
 		break;
 
 	case NSS_IPSECMGR_FLOW_TYPE_V6_SUBNET:
+		v6_subnet = &encap_flow->data.v6_subnet;
+
+		if (bitmap_empty((unsigned long *)v6_subnet->dst_subnet, NSS_NLIPV6_SUBNET_BITS) &&
+				!bitmap_empty((unsigned long *)v6_subnet->dst_mask, NSS_NLIPV6_SUBNET_BITS)) {
+			nss_nl_error("Invalid v6 dst_subnet\n");
+			return -1;
+		}
+		break;
+
 	default:
 		nss_nl_error("Invalid flow type\n");
 		return -1;
@@ -259,12 +269,12 @@
 	case NSS_IPSECMGR_SA_TYPE_V6:
 		v6 = &encap_sa->data.v6;
 
-		if (!bitmap_empty((unsigned long *)v6->src_ip, NSS_NLIPV6_ADDR_BITS)) {
+		if (bitmap_empty((unsigned long *)v6->src_ip, NSS_NLIPV6_ADDR_BITS)) {
 			nss_nl_error("Invalid V6 src_ip\n");
 			return -2;
 		}
 
-		if (!bitmap_empty((unsigned long *)v6->dst_ip, NSS_NLIPV6_ADDR_BITS)) {
+		if (bitmap_empty((unsigned long *)v6->dst_ip, NSS_NLIPV6_ADDR_BITS)) {
 			nss_nl_error("Invalid V6 dst_ip\n");
 			return -2;
 		}
@@ -320,12 +330,12 @@
 	case NSS_IPSECMGR_SA_TYPE_V6:
 		v6 = &decap_sa->data.v6;
 
-		if (!bitmap_empty((unsigned long *)v6->src_ip, NSS_NLIPV6_ADDR_BITS)) {
+		if (bitmap_empty((unsigned long *)v6->src_ip, NSS_NLIPV6_ADDR_BITS)) {
 			nss_nl_error("Invalid V6 src_ip\n");
 			return -2;
 		}
 
-		if (!bitmap_empty((unsigned long *)v6->dst_ip, NSS_NLIPV6_ADDR_BITS)) {
+		if (bitmap_empty((unsigned long *)v6->dst_ip, NSS_NLIPV6_ADDR_BITS)) {
 			nss_nl_error("Invalid V6 dst_ip\n");
 			return -2;
 		}
@@ -360,6 +370,7 @@
 {
 	struct nss_ipsecmgr_encap_v4_tuple *v4_tuple;
 	struct nss_ipsecmgr_encap_v4_subnet *v4_subnet;
+	struct nss_ipsecmgr_encap_v6_subnet *v6_subnet;
 	struct nss_ipsecmgr_encap_v6_tuple *v6_tuple;
 	struct nss_ipsecmgr_sa_v4 *v4;
 	struct nss_ipsecmgr_sa_v6 *v6;
@@ -389,12 +400,12 @@
 	case NSS_IPSECMGR_FLOW_TYPE_V6_TUPLE:
 		v6_tuple = &encap_flow->data.v6_tuple;
 
-		if (!bitmap_empty((unsigned long *)v6_tuple->src_ip, NSS_NLIPV6_ADDR_BITS)) {
+		if (bitmap_empty((unsigned long *)v6_tuple->src_ip, NSS_NLIPV6_ADDR_BITS)) {
 			nss_nl_error("Invalid v6 src_ip\n");
 			return -1;
 		}
 
-		if (!bitmap_empty((unsigned long *)v6_tuple->dst_ip, NSS_NLIPV6_ADDR_BITS)) {
+		if (bitmap_empty((unsigned long *)v6_tuple->dst_ip, NSS_NLIPV6_ADDR_BITS)) {
 			nss_nl_error("Invalid v6 dest_ip\n");
 			return -1;
 		}
@@ -406,6 +417,14 @@
 		break;
 
 	case NSS_IPSECMGR_FLOW_TYPE_V6_SUBNET:
+		v6_subnet = &encap_flow->data.v6_subnet;
+		if (bitmap_empty((unsigned long *)v6_subnet->dst_subnet, NSS_NLIPV6_SUBNET_BITS) &&
+				!bitmap_empty((unsigned long *)v6_subnet->dst_mask, NSS_NLIPV6_SUBNET_BITS)) {
+			nss_nl_error("Invalid v6 dst_subnet\n");
+			return -1;
+		}
+		break;
+
 	default:
 		nss_nl_error("Invalid flow type\n");
 		return -1;
@@ -426,12 +445,12 @@
 	case NSS_IPSECMGR_SA_TYPE_V6:
 		v6 = &encap_sa->data.v6;
 
-		if (!bitmap_empty((unsigned long *)v6->src_ip, NSS_NLIPV6_ADDR_BITS)) {
+		if (bitmap_empty((unsigned long *)v6->src_ip, NSS_NLIPV6_ADDR_BITS)) {
 			nss_nl_error("Invalid V6 src_ip\n");
 			return -2;
 		}
 
-		if (!bitmap_empty((unsigned long *)v6->dst_ip, NSS_NLIPV6_ADDR_BITS)) {
+		if (bitmap_empty((unsigned long *)v6->dst_ip, NSS_NLIPV6_ADDR_BITS)) {
 			nss_nl_error("Invalid V6 dst_ip\n");
 			return -2;
 		}
@@ -472,12 +491,12 @@
 	case NSS_IPSECMGR_SA_TYPE_V6:
 		v6 = &encap_sa->data.v6;
 
-		if (!bitmap_empty((unsigned long *)v6->src_ip, NSS_NLIPV6_ADDR_BITS)) {
+		if (bitmap_empty((unsigned long *)v6->src_ip, NSS_NLIPV6_ADDR_BITS)) {
 			nss_nl_error("Invalid V6 src_ip\n");
 			return -2;
 		}
 
-		if (!bitmap_empty((unsigned long *)v6->dst_ip, NSS_NLIPV6_ADDR_BITS)) {
+		if (bitmap_empty((unsigned long *)v6->dst_ip, NSS_NLIPV6_ADDR_BITS)) {
 			nss_nl_error("Invalid V6 dst_ip\n");
 			return -2;
 		}
@@ -518,12 +537,12 @@
 	case NSS_IPSECMGR_SA_TYPE_V6:
 		v6 = &decap_sa->data.v6;
 
-		if (!bitmap_empty((unsigned long *)v6->src_ip, NSS_NLIPV6_ADDR_BITS)) {
+		if (bitmap_empty((unsigned long *)v6->src_ip, NSS_NLIPV6_ADDR_BITS)) {
 			nss_nl_error("Invalid V6 src_ip\n");
 			return -2;
 		}
 
-		if (!bitmap_empty((unsigned long *)v6->dst_ip, NSS_NLIPV6_ADDR_BITS)) {
+		if (bitmap_empty((unsigned long *)v6->dst_ip, NSS_NLIPV6_ADDR_BITS)) {
 			nss_nl_error("Invalid V6 dst_ip\n");
 			return -2;
 		}
diff --git a/netlink/nss_nlipv6.c b/netlink/nss_nlipv6.c
index a258af9..ece3145 100644
--- a/netlink/nss_nlipv6.c
+++ b/netlink/nss_nlipv6.c
@@ -141,16 +141,17 @@
 }
 
 /*
- * nss_nlipv6_get_addr_htonl()
+ * nss_nlipv6_get_addr_hton()
  * 	Convert the ipv6 address from host order to network order.
  */
-static inline void nss_nlipv6_get_addr_htonl(uint32_t src[4], uint32_t dst[4])
+static inline void nss_nlipv6_get_addr_hton(uint32_t src[4], uint32_t dst[4])
 {
-	int32_t i;
+	nss_nlipv6_swap_addr(src, dst);
 
-	for (i = 0; i < 4; i++) {
-		dst[i] = htonl(src[i]);
-	}
+	dst[0] = htonl(dst[0]);
+	dst[1] = htonl(dst[1]);
+	dst[2] = htonl(dst[2]);
+	dst[3] = htonl(dst[3]);
 }
 
 /*
@@ -167,7 +168,7 @@
 	struct neighbour *neigh;
 	struct  in6_addr addr;
 
-	nss_nlipv6_get_addr_htonl(ip_addr, addr.s6_addr32);
+	nss_nlipv6_get_addr_hton(ip_addr, addr.s6_addr32);
 
 	if (ipv6_addr_is_multicast(&addr)) {
 		/*
@@ -239,12 +240,12 @@
 		return -EINVAL;
 	}
 
-	if (!bitmap_empty((unsigned long *)tuple->flow_ip, NSS_NLIPV6_ADDR_BITS)) {
+	if (bitmap_empty((unsigned long *)tuple->flow_ip, NSS_NLIPV6_ADDR_BITS)) {
 		nss_nl_info("Empty flow IP\n");
 		return -EINVAL;
 	}
 
-	if (!bitmap_empty((unsigned long *)tuple->return_ip, NSS_NLIPV6_ADDR_BITS)) {
+	if (bitmap_empty((unsigned long *)tuple->return_ip, NSS_NLIPV6_ADDR_BITS)) {
 		nss_nl_info("Empty return IP\n");
 		return -EINVAL;
 	}
@@ -643,6 +644,12 @@
 			(void *)resp);				/* app context */
 
 	/*
+	 * Conver the IP addresses to NSS format
+	 */
+	nss_nlipv6_swap_addr(nim->msg.rule_create.tuple.flow_ip, nim->msg.rule_create.tuple.flow_ip);
+	nss_nlipv6_swap_addr(nim->msg.rule_create.tuple.return_ip, nim->msg.rule_create.tuple.return_ip);
+
+	/*
 	 * Push Rule to NSS
 	 */
 	tx_status = nss_ipv6_tx(gbl_ctx.nss, nim);
@@ -717,6 +724,12 @@
 			sizeof(struct nss_ipv6_rule_destroy_msg),	/* message size */
 			nss_nlipv6_process_resp,			/* response callback */
 			(void *)resp);					/* app context */
+	/*
+	 * Conver the IP addresses to NSS format
+	 */
+	nss_nlipv6_swap_addr(nim->msg.rule_destroy.tuple.flow_ip, nim->msg.rule_destroy.tuple.flow_ip);
+	nss_nlipv6_swap_addr(nim->msg.rule_destroy.tuple.return_ip, nim->msg.rule_destroy.tuple.return_ip);
+
 
 	/*
 	 * Push rule to NSS