[qca-nss-clients] IPv6IPsec: Added support for IPv6IPsec

Added support for IPv6IPsec in netlink and ipsecmgr.

Change-Id: Idfbc61a2253692621f9beb63c878f1a113fe5467
Signed-off-by: mandrw <mandrw@codeaurora.org>
diff --git a/ipsecmgr/nss_ipsecmgr.c b/ipsecmgr/nss_ipsecmgr.c
index b663dc9..c0165aa 100644
--- a/ipsecmgr/nss_ipsecmgr.c
+++ b/ipsecmgr/nss_ipsecmgr.c
@@ -283,7 +283,6 @@
 	default:
 		goto fail;
 	}
-
 	/*
 	 * check whether the IPsec encapsulation can be offloaded to NSS
 	 * 	if the flow matches a subnet rule, then a new flow rule is added to NSS
@@ -377,8 +376,8 @@
 {
 	struct nss_ipsecmgr_priv *priv;
 	nss_ipsecmgr_data_cb_t cb_fn;
+	uint16_t next_protocol;
 	void *cb_ctx;
-	struct iphdr *ip;
 
 	BUG_ON(dev == NULL);
 	BUG_ON(skb == NULL);
@@ -405,9 +404,21 @@
 		goto done;
 	}
 
-	ip = (struct iphdr *)skb->data;
-	if (unlikely((ip->version != IPVERSION) || (ip->ihl != 5))) {
-		nss_ipsecmgr_error("dropping packets(IP version:%x, Header len:%x)\n", ip->version, ip->ihl);
+	skb_reset_network_header(skb);
+
+	switch (ip_hdr(skb)->version) {
+	case IPVERSION:
+		skb->protocol = cpu_to_be16(ETH_P_IP);
+		next_protocol = ip_hdr(skb)->protocol;
+		break;
+
+	case 6:
+		skb->protocol = cpu_to_be16(ETH_P_IPV6);
+		next_protocol = ipv6_hdr(skb)->nexthdr;
+		break;
+
+	default:
+		nss_ipsecmgr_error("%p: Unsupported IP Version\n",  priv->nss_ctx);
 		dev_kfree_skb_any(skb);
 		goto done;
 	}
@@ -418,21 +429,17 @@
 	 * in Host we should flush the ENCAP rules and free the packet. This will force
 	 * subsequent packets to follow the Slow path IPsec thus recreating the rules
 	 */
-	if (unlikely(ip->protocol == IPPROTO_ESP)) {
-		/*
-		 * XXX: flush IPsec SA specific to this packet
-		 */
+	if (next_protocol == IPPROTO_ESP) {
 		dev_kfree_skb_any(skb);
 		goto done;
 	}
 
-	skb_reset_network_header(skb);
-	skb_reset_mac_header(skb);
-
 	skb->pkt_type = PACKET_HOST;
-	skb->protocol = cpu_to_be16(ETH_P_IP);
 	skb->skb_iif = dev->ifindex;
 
+	skb_reset_mac_header(skb);
+
+	/* Send the data up to Linux Stack */
 	netif_receive_skb(skb);
 done:
 	/* release the device as we are done */
@@ -477,7 +484,7 @@
 		/*
 		 * prepare and lookup sa based on selector sent from nss
 		 */
-		nss_ipsecmgr_v4_sa_sel2key(&nim->msg.sa_stats.sel, &key);
+		nss_ipsecmgr_sa_sel2key(&nim->msg.sa_stats.sel, &key);
 
 		write_lock(&priv->lock);
 
diff --git a/ipsecmgr/nss_ipsecmgr_flow.c b/ipsecmgr/nss_ipsecmgr_flow.c
index d2e7b7c..10c569a 100644
--- a/ipsecmgr/nss_ipsecmgr_flow.c
+++ b/ipsecmgr/nss_ipsecmgr_flow.c
@@ -157,9 +157,11 @@
 {
 	struct nss_ipsecmgr_priv *priv = app_data;
 	struct nss_ipsecmgr_flow_entry *flow;
+	struct nss_ipsec_rule_sel *sel;
 	struct nss_ipsecmgr_ref *ref;
 	struct nss_ipsecmgr_key key;
 	struct net_device *dev;
+	uint32_t interface;
 
 	if (nim->cm.response != NSS_CMN_RESPONSE_ACK) {
 		return;
@@ -170,16 +172,21 @@
 		return;
 	}
 
+	interface = nim->cm.interface;
+	sel = &nim->msg.flow_stats.sel;
+
 	/*
 	 * prepare key from selector
 	 */
-	switch (nim->cm.interface) {
+	switch (interface) {
 	case NSS_IPSEC_ENCAP_IF_NUMBER:
-		nss_ipsecmgr_encap_v4_sel2key(&nim->msg.flow_stats.sel, &key);
+		nss_ipsecmgr_encap_sel2key(sel, &key);
 		break;
+
 	case NSS_IPSEC_DECAP_IF_NUMBER:
-		nss_ipsecmgr_decap_v4_sel2key(&nim->msg.flow_stats.sel, &key);
+		nss_ipsecmgr_decap_sel2key(sel, &key);
 		break;
+
 	default:
 		goto done;
 	}
@@ -223,6 +230,7 @@
 	struct nss_ipsec_msg nim;
 	struct net_device *dev;
 	uint16_t interface;
+	uint32_t addr[4];
 	ssize_t ret = 0;
 	char *local;
 	char *type;
@@ -319,9 +327,20 @@
 
 	len = 0;
 	len += snprintf(local + len, NSS_IPSECMGR_MAX_BUF_SZ - len, "type:%s\n", type);
-	len += snprintf(local + len, NSS_IPSECMGR_MAX_BUF_SZ - len, "dst_ip: %pI4h\n", &flow_sel->ipv4_dst);
-	len += snprintf(local + len, NSS_IPSECMGR_MAX_BUF_SZ - len, "src_ip: %pI4h\n", &flow_sel->ipv4_src);
-	len += snprintf(local + len, NSS_IPSECMGR_MAX_BUF_SZ - len, "proto: %d\n", flow_sel->ipv4_proto);
+	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]);
+		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));
+		break;
+
+	}
+
+	len += snprintf(local + len, NSS_IPSECMGR_MAX_BUF_SZ - len, "proto: %d\n", flow_sel->proto_next_hdr);
 
 	/*
 	 * packet stats
@@ -367,9 +386,10 @@
 {
 	struct nss_ipsec_rule_sel *sel = &nim->msg.push.sel;
 
-	sel->ipv4_dst = flow->dst_ip;
-	sel->ipv4_src = flow->src_ip;
-	sel->ipv4_proto = flow->protocol;
+	sel->dst_addr[0] = flow->dst_ip;
+	sel->src_addr[0] = flow->src_ip;
+	sel->proto_next_hdr = flow->protocol;
+	sel->ip_ver = NSS_IPSEC_IPVER_4;
 
 	sel->esp_spi = 0;
 	sel->dst_port = 0;
@@ -384,10 +404,11 @@
 {
 	struct nss_ipsec_rule_sel *sel = &nim->msg.push.sel;
 
-	sel->ipv4_dst = flow->dst_ip;
-	sel->ipv4_src = flow->src_ip;
-	sel->ipv4_proto = IPPROTO_ESP;
+	sel->dst_addr[0] = flow->dst_ip;
+	sel->src_addr[0] = flow->src_ip;
+	sel->proto_next_hdr = IPPROTO_ESP;
 	sel->esp_spi = flow->spi_index;
+	sel->ip_ver = NSS_IPSEC_IPVER_4;
 
 	sel->dst_port = 0;
 	sel->src_port = 0;
@@ -406,7 +427,7 @@
 	nss_ipsecmgr_key_write_32(key, flow->dst_ip, NSS_IPSECMGR_KEY_POS_IPV4_DST);
 	nss_ipsecmgr_key_write_32(key, flow->src_ip, NSS_IPSECMGR_KEY_POS_IPV4_SRC);
 
-	key->len = NSS_IPSECMGR_KEY_LEN_ENCAP_IPV4_FLOW;
+	key->len = NSS_IPSECMGR_KEY_LEN_IPV4_ENCAP_FLOW;
 }
 
 /*
@@ -421,42 +442,170 @@
 	nss_ipsecmgr_key_write_8(key, IPPROTO_ESP, NSS_IPSECMGR_KEY_POS_IP_PROTO);
 	nss_ipsecmgr_key_write_32(key, flow->dst_ip, NSS_IPSECMGR_KEY_POS_IPV4_DST);
 	nss_ipsecmgr_key_write_32(key, flow->src_ip, NSS_IPSECMGR_KEY_POS_IPV4_SRC);
-	nss_ipsecmgr_key_write_32(key, flow->spi_index, NSS_IPSECMGR_KEY_POS_ESP_SPI);
+	nss_ipsecmgr_key_write_32(key, flow->spi_index, NSS_IPSECMGR_KEY_POS_IPV4_ESP_SPI);
 
-	key->len = NSS_IPSECMGR_KEY_LEN_DECAP_IPV4_FLOW;
+	key->len = NSS_IPSECMGR_KEY_LEN_IPV4_DECAP_FLOW;
 }
 
 /*
  * nss_ipsecmgr_encap_v4_sel2key()
  * 	convert a selector to key
  */
-void nss_ipsecmgr_encap_v4_sel2key(struct nss_ipsec_rule_sel *sel, struct nss_ipsecmgr_key *key)
+void nss_ipsecmgr_encap_sel2key(struct nss_ipsec_rule_sel *sel, struct nss_ipsecmgr_key *key)
 {
+	uint32_t i;
+
 	nss_ipsecmgr_key_reset(key);
+	switch (sel->ip_ver) {
+	case NSS_IPSEC_IPVER_4:
+		nss_ipsecmgr_key_write_8(key, 4 /* v4 */, NSS_IPSECMGR_KEY_POS_IP_VER);
+		nss_ipsecmgr_key_write_8(key, sel->proto_next_hdr, NSS_IPSECMGR_KEY_POS_IP_PROTO);
+		nss_ipsecmgr_key_write_32(key, nss_ipsecmgr_get_v4addr(sel->dst_addr), NSS_IPSECMGR_KEY_POS_IPV4_DST);
+		nss_ipsecmgr_key_write_32(key, nss_ipsecmgr_get_v4addr(sel->src_addr), NSS_IPSECMGR_KEY_POS_IPV4_SRC);
 
-	nss_ipsecmgr_key_write_8(key, 4 /* v4 */, NSS_IPSECMGR_KEY_POS_IP_VER);
-	nss_ipsecmgr_key_write_8(key, sel->ipv4_proto, NSS_IPSECMGR_KEY_POS_IP_PROTO);
-	nss_ipsecmgr_key_write_32(key, sel->ipv4_dst, NSS_IPSECMGR_KEY_POS_IPV4_DST);
-	nss_ipsecmgr_key_write_32(key, sel->ipv4_src, NSS_IPSECMGR_KEY_POS_IPV4_SRC);
+		key->len = NSS_IPSECMGR_KEY_LEN_IPV4_ENCAP_FLOW;
+		break;
 
-	key->len = NSS_IPSECMGR_KEY_LEN_ENCAP_IPV4_FLOW;
+	case NSS_IPSEC_IPVER_6:
+		nss_ipsecmgr_key_write_8(key, 6 /* v6 */, NSS_IPSECMGR_KEY_POS_IP_VER);
+		nss_ipsecmgr_key_write_8(key, sel->proto_next_hdr, NSS_IPSECMGR_KEY_POS_IP_PROTO);
+
+		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));
+		}
+
+		key->len = NSS_IPSECMGR_KEY_LEN_IPV6_ENCAP_FLOW;
+		break;
+
+	default:
+		nss_ipsecmgr_warn("%p:Invalid selector\n", sel);
+		return;
+	}
 }
 
 /*
- * nss_ipsecmgr_decap_v4_sel2key()
+ * nss_ipsecmgr_decap_sel2key()
  * 	convert a selector to key
  */
-void nss_ipsecmgr_decap_v4_sel2key(struct nss_ipsec_rule_sel *sel, struct nss_ipsecmgr_key *key)
+void nss_ipsecmgr_decap_sel2key(struct nss_ipsec_rule_sel *sel, struct nss_ipsecmgr_key *key)
 {
+	uint32_t i;
+
 	nss_ipsecmgr_key_reset(key);
 
-	nss_ipsecmgr_key_write_8(key, 4 /* v4 */, NSS_IPSECMGR_KEY_POS_IP_VER);
-	nss_ipsecmgr_key_write_8(key, IPPROTO_ESP, NSS_IPSECMGR_KEY_POS_IP_PROTO);
-	nss_ipsecmgr_key_write_32(key, sel->ipv4_dst, NSS_IPSECMGR_KEY_POS_IPV4_DST);
-	nss_ipsecmgr_key_write_32(key, sel->ipv4_src, NSS_IPSECMGR_KEY_POS_IPV4_SRC);
-	nss_ipsecmgr_key_write_32(key, sel->esp_spi, NSS_IPSECMGR_KEY_POS_ESP_SPI);
+	switch (sel->ip_ver) {
+	case NSS_IPSEC_IPVER_4:
+		nss_ipsecmgr_key_write_8(key, 4 /* v4 */, NSS_IPSECMGR_KEY_POS_IP_VER);
+		nss_ipsecmgr_key_write_8(key, IPPROTO_ESP, NSS_IPSECMGR_KEY_POS_IP_PROTO);
+		nss_ipsecmgr_key_write_32(key, nss_ipsecmgr_get_v4addr(sel->dst_addr), NSS_IPSECMGR_KEY_POS_IPV4_DST);
+		nss_ipsecmgr_key_write_32(key, nss_ipsecmgr_get_v4addr(sel->src_addr), NSS_IPSECMGR_KEY_POS_IPV4_SRC);
+		nss_ipsecmgr_key_write_32(key, sel->esp_spi, NSS_IPSECMGR_KEY_POS_IPV4_ESP_SPI);
 
-	key->len = NSS_IPSECMGR_KEY_LEN_DECAP_IPV4_FLOW;
+		key->len = NSS_IPSECMGR_KEY_LEN_IPV4_DECAP_FLOW;
+		break;
+
+	case NSS_IPSEC_IPVER_6:
+		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);
+
+		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));
+		}
+
+		nss_ipsecmgr_key_write_32(key, sel->esp_spi, NSS_IPSECMGR_KEY_POS_IPV6_ESP_SPI);
+
+		key->len = NSS_IPSECMGR_KEY_LEN_IPV6_DECAP_FLOW;
+		break;
+
+	default:
+		nss_ipsecmgr_warn("%p:Invalid selector\n", sel);
+		return;
+	}
+}
+
+/*
+ * nss_ipsecmgr_copy_encap_v6_flow()
+ * 	copy flow data into the selector
+ */
+void nss_ipsecmgr_copy_encap_v6_flow(struct nss_ipsec_msg *nim, struct nss_ipsecmgr_encap_v6_tuple *flow)
+{
+	struct nss_ipsec_rule_sel *sel = &nim->msg.push.sel;
+
+	memcpy(sel->src_addr, flow->src_ip, sizeof(uint32_t) * 4);
+	memcpy(sel->dst_addr, flow->dst_ip, sizeof(uint32_t) * 4);
+
+	sel->proto_next_hdr = flow->next_hdr;
+	sel->ip_ver = NSS_IPSEC_IPVER_6;
+
+	sel->esp_spi = 0;
+	sel->dst_port = 0;
+	sel->src_port = 0;
+}
+
+/*
+ * nss_ipsecmgr_copy_decap_v6_flow()
+ * 	copy decap flow
+ */
+void nss_ipsecmgr_copy_decap_v6_flow(struct nss_ipsec_msg *nim, struct nss_ipsecmgr_sa_v6 *flow)
+{
+	struct nss_ipsec_rule_sel *sel = &nim->msg.push.sel;
+
+	memcpy(sel->src_addr, flow->src_ip, sizeof(uint32_t) * 4);
+	memcpy(sel->dst_addr, flow->dst_ip, sizeof(uint32_t) * 4);
+
+	sel->esp_spi = flow->spi_index;
+	sel->ip_ver = NSS_IPSEC_IPVER_6;
+
+	sel->proto_next_hdr = IPPROTO_ESP;
+
+	sel->dst_port = 0;
+	sel->src_port = 0;
+}
+
+/*
+ * nss_ipsecmgr_encap_v6_flow2key()
+ * 	convert an encap v6_flow into a key
+ */
+void nss_ipsecmgr_encap_v6_flow2key(struct nss_ipsecmgr_encap_v6_tuple *flow, struct nss_ipsecmgr_key *key)
+{
+	uint32_t i;
+
+	nss_ipsecmgr_key_reset(key);
+
+	nss_ipsecmgr_key_write_8(key, 6 /* v6 */, NSS_IPSECMGR_KEY_POS_IP_VER);
+	nss_ipsecmgr_key_write_8(key, flow->next_hdr, NSS_IPSECMGR_KEY_POS_IP_PROTO);
+
+	for (i  = 0; i < 4; i++) {
+		nss_ipsecmgr_key_write_32(key, flow->dst_ip[i], NSS_IPSECMGR_KEY_POS_IPV6_DST + (i * 32));
+		nss_ipsecmgr_key_write_32(key, flow->src_ip[i], NSS_IPSECMGR_KEY_POS_IPV6_SRC + (i * 32));
+	}
+
+	key->len = NSS_IPSECMGR_KEY_LEN_IPV6_ENCAP_FLOW;
+}
+
+/*
+ * nss_ipsecmgr_decap_v6_flow2key()
+ * 	convert a decap flow into a key
+ */
+void nss_ipsecmgr_decap_v6_flow2key(struct nss_ipsecmgr_sa_v6 *flow, struct nss_ipsecmgr_key *key)
+{
+	uint32_t i;
+
+	nss_ipsecmgr_key_reset(key);
+
+	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);
+
+	for (i  = 0; i < 4; i++) {
+		nss_ipsecmgr_key_write_32(key, flow->dst_ip[i], NSS_IPSECMGR_KEY_POS_IPV6_DST + (i * 32));
+		nss_ipsecmgr_key_write_32(key, flow->src_ip[i], NSS_IPSECMGR_KEY_POS_IPV6_SRC + (i * 32));
+	}
+
+	nss_ipsecmgr_key_write_32(key, flow->spi_index, NSS_IPSECMGR_KEY_POS_IPV6_ESP_SPI);
+
+	key->len = NSS_IPSECMGR_KEY_LEN_IPV6_DECAP_FLOW;
 }
 
 /*
@@ -576,7 +725,7 @@
 		sel = &nim.msg.push.sel;
 
 		nss_ipsecmgr_v4_hdr2sel(ip_hdr(skb), sel);
-		nss_ipsecmgr_encap_v4_sel2key(sel, &flow_key);
+		nss_ipsecmgr_encap_sel2key(sel, &flow_key);
 
 		/*
 		 * flow lookup is done with read lock
@@ -636,6 +785,28 @@
 
 		break;
 
+	case htons(ETH_P_IPV6):
+		sel = &nim.msg.push.sel;
+
+		nss_ipsecmgr_v6_hdr2sel((struct ipv6hdr *)skb_network_header(skb), sel);
+		nss_ipsecmgr_encap_sel2key(sel, &flow_key);
+
+		/*
+		 * flow lookup is done with read lock
+		 */
+		read_lock(&priv->lock);
+		flow_ref = nss_ipsecmgr_flow_lookup(priv, &flow_key);
+		read_unlock(&priv->lock);
+
+		/*
+		 * if flow is found then proceed with the TX
+		 */
+		if (!flow_ref) {
+			return false;
+		}
+
+		break;
+
 	default:
 		nss_ipsecmgr_warn("%p:protocol(%d) offload not supported\n", priv->dev, ntohs(skb->protocol));
 		return false;
diff --git a/ipsecmgr/nss_ipsecmgr_priv.h b/ipsecmgr/nss_ipsecmgr_priv.h
index afae613..e9f94d3 100644
--- a/ipsecmgr/nss_ipsecmgr_priv.h
+++ b/ipsecmgr/nss_ipsecmgr_priv.h
@@ -52,7 +52,8 @@
 
 #endif /* !CONFIG_DYNAMIC_DEBUG */
 #define NSS_IPSECMGR_MAX_KEY_NAME 64 /* bytes */
-#define NSS_IPSECMGR_MAX_KEY_WORDS 8 /* word */
+#define NSS_IPSECMGR_MAX_KEY_WORDS 16 /* word */
+
 #define NSS_IPSECMGR_MAX_KEY_BYTES (NSS_IPSECMGR_MAX_KEY_WORDS * sizeof(uint32_t))
 #define NSS_IPSECMGR_MAX_KEY_BITS (NSS_IPSECMGR_MAX_KEY_WORDS * sizeof(uint32_t) * BITS_PER_BYTE)
 #define NSS_IPSECMGR_CHK_POW2(x) (__builtin_constant_p(x) && !(~(x - 1) & (x >> 1)))
@@ -98,8 +99,11 @@
 	NSS_IPSECMGR_KEY_LEN_NONE = 0,
 	NSS_IPSECMGR_KEY_LEN_IPV4_SA = 4,		/* 16 bytes */
 	NSS_IPSECMGR_KEY_LEN_IPV4_SUBNET = 2,		/* 8 bytes */
-	NSS_IPSECMGR_KEY_LEN_ENCAP_IPV4_FLOW = 3,	/* 12 bytes */
-	NSS_IPSECMGR_KEY_LEN_DECAP_IPV4_FLOW = 4,	/* 16 bytes */
+	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_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
 };
 
@@ -111,18 +115,23 @@
 	NSS_IPSECMGR_KEY_POS_IP_PROTO = 8,		/* IPv4 Proto, bits[8:15] */
 	NSS_IPSECMGR_KEY_POS_IPV4_DST = 32,		/* IPv4 DST, bits[32:63] */
 	NSS_IPSECMGR_KEY_POS_IPV4_SRC = 64,		/* IPv4 SIP, bits[64:95] */
-	NSS_IPSECMGR_KEY_POS_ESP_SPI = 96,		/* ESP SPI, bits[96:127] */
+	NSS_IPSECMGR_KEY_POS_IPV4_ESP_SPI = 96,		/* IPv4 ESP SPI, bits[96:127] */
+	NSS_IPSECMGR_KEY_POS_IPV6_DST = 32,		/* IPv6 DST, bits[32:159] */
+	NSS_IPSECMGR_KEY_POS_IPV6_SRC = 160,	 	/* IPv6 SIP, bits[160:287] */
+	NSS_IPSECMGR_KEY_POS_IPV6_ESP_SPI = 288,	/* IPv6 ESP SPI, bits[288:319] */
+
+
 };
 
 /*
  * bits to mask within a key_word data, min - 0 & max - 31
  */
 enum nss_ipsecmgr_key_mask {
-	NSS_IPSECMGR_KEY_MASK_IP_VER = NSS_IPSECMGR_GENMASK(7, 0),		/* IP version, #bits - 8 */
+	NSS_IPSECMGR_KEY_MASK_IP_VER = NSS_IPSECMGR_GENMASK(7, 0),	/* IP version, #bits - 8 */
 	NSS_IPSECMGR_KEY_MASK_IP_PROTO = NSS_IPSECMGR_GENMASK(15, 8),	/* IP protocol, #bits - 8 */
 	NSS_IPSECMGR_KEY_MASK_IPV4_DST = NSS_IPSECMGR_GENMASK(31, 0),	/* IPv4 dst, #bits - 32 */
 	NSS_IPSECMGR_KEY_MASK_IPV4_SRC = NSS_IPSECMGR_GENMASK(31, 0),	/* IPv4 src, #bits - 32 */
-	NSS_IPSECMGR_KEY_MASK_ESP_SPI = NSS_IPSECMGR_GENMASK(31, 0),		/* ESP spi #bits - 32 */
+	NSS_IPSECMGR_KEY_MASK_ESP_SPI = NSS_IPSECMGR_GENMASK(31, 0),	/* ESP spi #bits - 32 */
 };
 
 struct nss_ipsecmgr_ref;
@@ -757,9 +766,47 @@
  */
 static inline void nss_ipsecmgr_v4_hdr2sel(struct iphdr *iph, struct nss_ipsec_rule_sel *sel)
 {
-	sel->ipv4_dst = ntohl(iph->daddr);
-	sel->ipv4_src = ntohl(iph->saddr);
-	sel->ipv4_proto = iph->protocol;
+	sel->dst_addr[0] = ntohl(iph->daddr);
+	sel->src_addr[0] = ntohl(iph->saddr);
+	sel->proto_next_hdr = iph->protocol;
+	sel->ip_ver = NSS_IPSEC_IPVER_4;
+}
+
+/*
+ * nss_ipsecmgr_v6addr_ntohl()
+ * 	convert the v6 address to
+ */
+static inline uint32_t *nss_ipsecmgr_v6addr_ntohl(uint32_t src[], uint32_t dst[])
+{
+	int i = 4;
+
+	while (i--) {
+		dst[i] = ntohl(src[i]);
+	}
+
+	return dst;
+}
+
+/*
+ * nss_ipsecmgr_v6_hdr2sel()
+ * 	convert v6_hdr to message sel
+ */
+static inline void nss_ipsecmgr_v6_hdr2sel(struct ipv6hdr *iph, struct nss_ipsec_rule_sel *sel)
+{
+	nss_ipsecmgr_v6addr_ntohl(iph->daddr.s6_addr32, sel->dst_addr);
+	nss_ipsecmgr_v6addr_ntohl(iph->saddr.s6_addr32, sel->src_addr);
+
+	sel->proto_next_hdr = iph->nexthdr;
+	sel->ip_ver = NSS_IPSEC_IPVER_6;
+}
+
+/*
+ * nss_ipsecmgr_get_ipv4_addr()
+ * 	Return ipv4 part of the address.
+ */
+static inline uint32_t nss_ipsecmgr_get_v4addr(uint32_t *addr)
+{
+	return addr[0];
 }
 
 /*
@@ -775,16 +822,20 @@
  * Encap flow API(s)
  */
 void nss_ipsecmgr_copy_encap_v4_flow(struct nss_ipsec_msg *nim, struct nss_ipsecmgr_encap_v4_tuple *flow);
+void nss_ipsecmgr_copy_encap_v6_flow(struct nss_ipsec_msg *nim, struct nss_ipsecmgr_encap_v6_tuple *flow);
 void nss_ipsecmgr_encap_v4_flow2key(struct nss_ipsecmgr_encap_v4_tuple *flow, struct nss_ipsecmgr_key *key);
-void nss_ipsecmgr_encap_v4_sel2key(struct nss_ipsec_rule_sel *sel, struct nss_ipsecmgr_key *key);
+void nss_ipsecmgr_encap_v6_flow2key(struct nss_ipsecmgr_encap_v6_tuple *flow, struct nss_ipsecmgr_key *key);
+void nss_ipsecmgr_encap_sel2key(struct nss_ipsec_rule_sel *sel, struct nss_ipsecmgr_key *key);
 void nss_ipsecmgr_encap_flow_init(struct nss_ipsec_msg *nim, enum nss_ipsec_msg_type type, struct nss_ipsecmgr_priv *priv);
 
 /*
  * Decap flow API(s)
  */
 void nss_ipsecmgr_copy_decap_v4_flow(struct nss_ipsec_msg *nim, struct nss_ipsecmgr_sa_v4 *flow);
+void nss_ipsecmgr_copy_decap_v6_flow(struct nss_ipsec_msg *nim, struct nss_ipsecmgr_sa_v6 *flow);
 void nss_ipsecmgr_decap_v4_flow2key(struct nss_ipsecmgr_sa_v4 *flow, struct nss_ipsecmgr_key *key);
-void nss_ipsecmgr_decap_v4_sel2key(struct nss_ipsec_rule_sel *sel, struct nss_ipsecmgr_key *key);
+void nss_ipsecmgr_decap_v6_flow2key(struct nss_ipsecmgr_sa_v6 *flow, struct nss_ipsecmgr_key *key);
+void nss_ipsecmgr_decap_sel2key(struct nss_ipsec_rule_sel *sel, struct nss_ipsecmgr_key *key);
 void nss_ipsecmgr_decap_flow_init(struct nss_ipsec_msg *nim, enum nss_ipsec_msg_type type, struct nss_ipsecmgr_priv *priv);
 
 /*
@@ -812,9 +863,11 @@
  * SA API(s)
  */
 void nss_ipsecmgr_copy_v4_sa(struct nss_ipsec_msg *nim, struct nss_ipsecmgr_sa_v4 *sa);
+void nss_ipsecmgr_copy_v6_sa(struct nss_ipsec_msg *nim, struct nss_ipsecmgr_sa_v6 *sa);
 void nss_ipsecmgr_copy_sa_data(struct nss_ipsec_msg *nim, struct nss_ipsecmgr_sa_data *data);
 void nss_ipsecmgr_v4_sa2key(struct nss_ipsecmgr_sa_v4 *sa, struct nss_ipsecmgr_key *key);
-void nss_ipsecmgr_v4_sa_sel2key(struct nss_ipsec_rule_sel *sel, struct nss_ipsecmgr_key *key);
+void nss_ipsecmgr_v6_sa2key(struct nss_ipsecmgr_sa_v6 *sa, struct nss_ipsecmgr_key *key);
+void nss_ipsecmgr_sa_sel2key(struct nss_ipsec_rule_sel *sel, struct nss_ipsecmgr_key *key);
 struct rtnl_link_stats64 *nss_ipsecmgr_sa_stats_all(struct nss_ipsecmgr_priv *priv, struct rtnl_link_stats64 *stats);
 void nss_ipsecmgr_sa_stats_update(struct nss_ipsec_msg *nim, struct nss_ipsecmgr_sa_entry *sa);
 
diff --git a/ipsecmgr/nss_ipsecmgr_sa.c b/ipsecmgr/nss_ipsecmgr_sa.c
index 405f22c..4b16b7d 100644
--- a/ipsecmgr/nss_ipsecmgr_sa.c
+++ b/ipsecmgr/nss_ipsecmgr_sa.c
@@ -89,6 +89,7 @@
 	struct nss_ipsecmgr_ref *ref;
 	struct net_device *dev;
 	char *local, *type;
+	uint32_t addr[4];
 	ssize_t ret = 0;
 	int len;
 
@@ -126,10 +127,20 @@
 
 	len = 0;
 	len += snprintf(local + len, NSS_IPSECMGR_MAX_BUF_SZ - len, "type:%s\n", type);
-	len += snprintf(local + len, NSS_IPSECMGR_MAX_BUF_SZ - len, "dst_ip: %pI4h\n", &oip->ipv4_dst);
-	len += snprintf(local + len, NSS_IPSECMGR_MAX_BUF_SZ - len, "src_ip: %pI4h\n", &oip->ipv4_src);
+	switch (oip->ip_ver) {
+	case NSS_IPSEC_IPVER_4:
+		len += snprintf(local + len, NSS_IPSECMGR_MAX_BUF_SZ - len, "dst_ip: %pI4h\n", &oip->dst_addr[0]);
+		len += snprintf(local + len, NSS_IPSECMGR_MAX_BUF_SZ - len, "src_ip: %pI4h\n", &oip->src_addr[0]);
+		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));
+		break;
+
+	}
 	len += snprintf(local + len, NSS_IPSECMGR_MAX_BUF_SZ - len, "spi_idx: 0x%x\n", oip->esp_spi);
-	len += snprintf(local + len, NSS_IPSECMGR_MAX_BUF_SZ - len, "ttl: %d\n", oip->ipv4_ttl);
+	len += snprintf(local + len, NSS_IPSECMGR_MAX_BUF_SZ - len, "ttl: %d\n", oip->ttl_hop_limit);
 
 	/*
 	 * packet stats
@@ -401,10 +412,30 @@
 {
 	struct nss_ipsec_rule_oip *oip = &nim->msg.push.oip;
 
-	oip->ipv4_dst = sa->dst_ip;
-	oip->ipv4_src = sa->src_ip;
-	oip->ipv4_ttl = sa->ttl;
+	oip->dst_addr[0] = sa->dst_ip;
+	oip->src_addr[0] = sa->src_ip;
+	oip->ttl_hop_limit = sa->ttl;
 	oip->esp_spi = sa->spi_index;
+	oip->ip_ver = NSS_IPSEC_IPVER_4;
+}
+
+/*
+ * nss_ipsecmgr_copy_v6_sa()
+ * 	update the SA entry with the SA data
+ */
+void nss_ipsecmgr_copy_v6_sa(struct nss_ipsec_msg *nim, struct nss_ipsecmgr_sa_v6 *sa)
+{
+	struct nss_ipsec_rule_oip *oip = &nim->msg.push.oip;
+
+	/*
+	 * copy outer header
+	 */
+	memcpy(oip->dst_addr, sa->dst_ip, sizeof(uint32_t) * 4);
+	memcpy(oip->src_addr, sa->src_ip, sizeof(uint32_t) * 4);
+
+	oip->esp_spi = sa->spi_index;
+	oip->ttl_hop_limit = sa->hop_limit;
+	oip->ip_ver = NSS_IPSEC_IPVER_6;
 }
 
 /*
@@ -439,26 +470,68 @@
 	nss_ipsecmgr_key_write_8(key, IPPROTO_ESP, NSS_IPSECMGR_KEY_POS_IP_PROTO);
 	nss_ipsecmgr_key_write_32(key, sa->dst_ip, NSS_IPSECMGR_KEY_POS_IPV4_DST);
 	nss_ipsecmgr_key_write_32(key, sa->src_ip, NSS_IPSECMGR_KEY_POS_IPV4_SRC);
-	nss_ipsecmgr_key_write_32(key, sa->spi_index, NSS_IPSECMGR_KEY_POS_ESP_SPI);
+	nss_ipsecmgr_key_write_32(key, sa->spi_index, NSS_IPSECMGR_KEY_POS_IPV4_ESP_SPI);
 
 	key->len = NSS_IPSECMGR_KEY_LEN_IPV4_SA;
 }
 
 /*
- * nss_ipsecmgr_v4_sa_sel2key()
+ * nss_ipsecmgr_sa_sel2key()
  * 	convert a SA into a key
  */
-void nss_ipsecmgr_v4_sa_sel2key(struct nss_ipsec_rule_sel *sel, struct nss_ipsecmgr_key *key)
+void nss_ipsecmgr_sa_sel2key(struct nss_ipsec_rule_sel *sel, struct nss_ipsecmgr_key *key)
 {
+	uint32_t i;
+
 	nss_ipsecmgr_key_reset(key);
 
-	nss_ipsecmgr_key_write_8(key, 4 /* v4 */, NSS_IPSECMGR_KEY_POS_IP_VER);
-	nss_ipsecmgr_key_write_8(key, IPPROTO_ESP, NSS_IPSECMGR_KEY_POS_IP_PROTO);
-	nss_ipsecmgr_key_write_32(key, sel->ipv4_dst, NSS_IPSECMGR_KEY_POS_IPV4_DST);
-	nss_ipsecmgr_key_write_32(key, sel->ipv4_src, NSS_IPSECMGR_KEY_POS_IPV4_SRC);
-	nss_ipsecmgr_key_write_32(key, sel->esp_spi, NSS_IPSECMGR_KEY_POS_ESP_SPI);
+	switch (sel->ip_ver) {
+	case NSS_IPSEC_IPVER_4:
+		nss_ipsecmgr_key_write_8(key, 4 /* v4 */, NSS_IPSECMGR_KEY_POS_IP_VER);
+		nss_ipsecmgr_key_write_8(key, IPPROTO_ESP, NSS_IPSECMGR_KEY_POS_IP_PROTO);
+		nss_ipsecmgr_key_write_32(key, nss_ipsecmgr_get_v4addr(sel->dst_addr), NSS_IPSECMGR_KEY_POS_IPV4_DST);
+		nss_ipsecmgr_key_write_32(key, nss_ipsecmgr_get_v4addr(sel->src_addr), NSS_IPSECMGR_KEY_POS_IPV4_SRC);
+		nss_ipsecmgr_key_write_32(key, sel->esp_spi, NSS_IPSECMGR_KEY_POS_IPV4_ESP_SPI);
 
-	key->len = NSS_IPSECMGR_KEY_LEN_IPV4_SA;
+		key->len = NSS_IPSECMGR_KEY_LEN_IPV4_SA;
+		break;
+
+	case NSS_IPSEC_IPVER_6:
+		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);
+
+		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));
+		}
+
+		nss_ipsecmgr_key_write_32(key, sel->esp_spi, NSS_IPSECMGR_KEY_POS_IPV6_ESP_SPI);
+		key->len = NSS_IPSECMGR_KEY_LEN_IPV6_SA;
+		break;
+	}
+}
+
+/*
+ * nss_ipsecmgr_v6_sa2key()
+ * 	convert a SA into a key
+ */
+void nss_ipsecmgr_v6_sa2key(struct nss_ipsecmgr_sa_v6 *sa, struct nss_ipsecmgr_key *key)
+{
+	uint32_t i;
+
+	nss_ipsecmgr_key_reset(key);
+
+	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);
+
+	for (i  = 0; i < 4; i++) {
+		nss_ipsecmgr_key_write_32(key, sa->dst_ip[i], NSS_IPSECMGR_KEY_POS_IPV6_DST + (i * 32));
+		nss_ipsecmgr_key_write_32(key, sa->src_ip[i], NSS_IPSECMGR_KEY_POS_IPV6_SRC + (i * 32));
+	}
+
+	nss_ipsecmgr_key_write_32(key, sa->spi_index, NSS_IPSECMGR_KEY_POS_IPV6_ESP_SPI);
+
+	key->len = NSS_IPSECMGR_KEY_LEN_IPV6_SA;
 }
 
 /*
@@ -641,6 +714,19 @@
 		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);
+		nss_ipsecmgr_copy_v6_sa(&info.nim, &sa->data.v6);
+		nss_ipsecmgr_copy_sa_data(&info.nim, data);
+
+		nss_ipsecmgr_encap_v6_flow2key(&flow->data.v6_tuple, &info.child_key);
+		nss_ipsecmgr_v6_sa2key(&sa->data.v6, &info.sa_key);
+
+		info.child_alloc = nss_ipsecmgr_flow_alloc;
+		info.child_lookup = nss_ipsecmgr_flow_lookup;
+		break;
+
 
 	default:
 		nss_ipsecmgr_warn("%p:unknown flow type(%d)\n", tun, flow->type);
@@ -693,6 +779,18 @@
 		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);
+		nss_ipsecmgr_copy_v6_sa(&info.nim, &sa->data.v6);
+
+		nss_ipsecmgr_encap_v6_flow2key(&flow->data.v6_tuple, &info.child_key);
+		nss_ipsecmgr_v6_sa2key(&sa->data.v6, &info.sa_key);
+
+		info.child_alloc = nss_ipsecmgr_flow_alloc;
+		info.child_lookup = nss_ipsecmgr_flow_lookup;
+		break;
+
 	default:
 		nss_ipsecmgr_warn("%p:unknown flow type(%d)\n", tun, flow->type);
 		return false;
@@ -703,7 +801,6 @@
 }
 EXPORT_SYMBOL(nss_ipsecmgr_encap_del);
 
-
 /*
  * nss_ipsecmgr_decap_add()
  * 	add decap flow/subnet to an existing or new SA
@@ -731,6 +828,16 @@
 		nss_ipsecmgr_v4_sa2key(&sa->data.v4, &info.sa_key);
 		break;
 
+	case NSS_IPSECMGR_SA_TYPE_V6:
+
+		nss_ipsecmgr_copy_decap_v6_flow(&info.nim, &sa->data.v6);
+		nss_ipsecmgr_copy_v6_sa(&info.nim, &sa->data.v6);
+		nss_ipsecmgr_copy_sa_data(&info.nim, data);
+
+		nss_ipsecmgr_decap_v6_flow2key(&sa->data.v6, &info.child_key);
+		nss_ipsecmgr_v6_sa2key(&sa->data.v6, &info.sa_key);
+		break;
+
 	default:
 		nss_ipsecmgr_warn("%p:unknown flow type(%d)\n", tun, sa->type);
 		return false;
@@ -759,6 +866,10 @@
 		nss_ipsecmgr_v4_sa2key(&sa->data.v4, &sa_key);
 		break;
 
+	case NSS_IPSECMGR_SA_TYPE_V6:
+		nss_ipsecmgr_v6_sa2key(&sa->data.v6, &sa_key);
+		break;
+
 	default:
 		nss_ipsecmgr_warn("%p:Unsupported sa type (type - %d)\n", tun, sa->type);
 		return false;
diff --git a/ipsecmgr/nss_ipsecmgr_subnet.c b/ipsecmgr/nss_ipsecmgr_subnet.c
index baf6e0c..db6baea 100644
--- a/ipsecmgr/nss_ipsecmgr_subnet.c
+++ b/ipsecmgr/nss_ipsecmgr_subnet.c
@@ -297,8 +297,8 @@
 	nss_ipsecmgr_key_reset(key);
 
 	nss_ipsecmgr_key_write_8(key, 4 /* ipv4 */, NSS_IPSECMGR_KEY_POS_IP_VER);
-	nss_ipsecmgr_key_write_8(key, sel->ipv4_proto, NSS_IPSECMGR_KEY_POS_IP_PROTO);
-	nss_ipsecmgr_key_write_32(key, sel->ipv4_dst, NSS_IPSECMGR_KEY_POS_IPV4_DST);
+	nss_ipsecmgr_key_write_8(key, sel->proto_next_hdr, NSS_IPSECMGR_KEY_POS_IP_PROTO);
+	nss_ipsecmgr_key_write_32(key, nss_ipsecmgr_get_v4addr(sel->dst_addr), NSS_IPSECMGR_KEY_POS_IPV4_DST);
 
 	key->len = NSS_IPSECMGR_KEY_LEN_IPV4_SUBNET;
 }
diff --git a/netlink/Makefile b/netlink/Makefile
index 8dba681..8b2fb2a 100755
--- a/netlink/Makefile
+++ b/netlink/Makefile
@@ -4,6 +4,7 @@
 
 ccflags-y += -DCONFIG_NSS_NLCRYPTO=1
 ccflags-y += -DCONFIG_NSS_NLIPV4=1
+ccflags-y += -DCONFIG_NSS_NLIPV6=1
 ccflags-y += -DCONFIG_NSS_NLIPSEC=1
 ccflags-y += -DCONFIG_NSS_NLOAM=1
 
@@ -12,5 +13,6 @@
 qca-nss-netlink-objs := nss_nl.o
 qca-nss-netlink-objs += nss_nlcrypto.o
 qca-nss-netlink-objs += nss_nlipv4.o
+qca-nss-netlink-objs += nss_nlipv6.o
 qca-nss-netlink-objs += nss_nlipsec.o
 qca-nss-netlink-objs += nss_nloam.o
diff --git a/netlink/include/nss_nlipv6_if.h b/netlink/include/nss_nlipv6_if.h
new file mode 100644
index 0000000..a26c816
--- /dev/null
+++ b/netlink/include/nss_nlipv6_if.h
@@ -0,0 +1,55 @@
+/*
+ **************************************************************************
+ * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ * Permission to use, copy, modify, and/or distribute this software for
+ * any purpose with or without fee is hereby granted, provided that the
+ * above copyright notice and this permission notice appear in all copies.
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ **************************************************************************
+ */
+
+/*
+ * @file nss_nlipv6_if.h
+ *	NSS Netlink IPv6 headers
+ */
+#ifndef __NSS_NLIPV6_IF_H
+#define __NSS_NLIPV6_IF_H
+
+/**
+ * IPv6 forwarding Family
+ */
+#define NSS_NLIPV6_FAMILY "nss_nlipv6"
+
+#define NSS_NLIPV6_ADDR_BITS (sizeof(uint32_t) * 4)
+
+/**
+ * @brief IPv6 rule
+ */
+struct nss_nlipv6_rule {
+	struct nss_nlcmn cm;		/**< common message header */
+
+	char flow_ifname[IFNAMSIZ];	/**< ingress interface name */
+	char return_ifname[IFNAMSIZ];	/**< egress interface name */
+
+	struct nss_ipv6_msg nim;	/**< rule message */
+};
+
+/**
+ * @brief NETLINK IPv6 message init
+ *
+ * @param rule[IN] NSS NETLINK IPv6 rule
+ * @param type[IN] IPv6 message type
+ */
+static inline void nss_nlipv6_rule_init(struct nss_nlipv6_rule *rule, enum nss_ipv6_message_types type)
+{
+	nss_nlcmn_set_ver(&rule->cm, NSS_NL_VER);
+	nss_nlcmn_init_cmd(&rule->cm, sizeof(struct nss_nlipv6_rule), type);
+}
+
+#endif /* __NSS_NLIPV6_IF_H */
diff --git a/netlink/nss_nl.c b/netlink/nss_nl.c
index 8fa3646..e1097da 100755
--- a/netlink/nss_nl.c
+++ b/netlink/nss_nl.c
@@ -29,12 +29,14 @@
 #include "nss_nlcmn_if.h"
 #include "nss_crypto_if.h"
 #include "nss_nlipv4_if.h"
+#include "nss_nlipv6_if.h"
 #include "nss_nlcrypto_if.h"
 #include "nss_nlipsec_if.h"
 #include "nss_nloam_if.h"
 
 #include "nss_nl.h"
 #include "nss_nlipv4.h"
+#include "nss_nlipv6.h"
 #include "nss_nlcrypto.h"
 #include "nss_nlipsec.h"
 #include "nss_nloam.h"
@@ -94,6 +96,16 @@
 		.exit = NSS_NLOAM_EXIT,			/* exit */
 		.valid = CONFIG_NSS_NLOAM		/* 1 or 0 */
 	},
+	{
+		/*
+		 * NSS_NLIPV6
+		 */
+		.name = NSS_NLIPV6_FAMILY,		/* ipv6 */
+		.entry = NSS_NLIPV6_INIT,		/* init */
+		.exit = NSS_NLIPV6_EXIT,		/* exit */
+		.valid = CONFIG_NSS_NLIPV6		/* 1 or 0 */
+	},
+
 };
 
 #define NSS_NL_FAMILY_HANDLER_SZ ARRAY_SIZE(family_handlers)
diff --git a/netlink/nss_nlipsec.c b/netlink/nss_nlipsec.c
index 0dae097..b5ccdf6 100644
--- a/netlink/nss_nlipsec.c
+++ b/netlink/nss_nlipsec.c
@@ -40,6 +40,7 @@
 #include "nss_nl.h"
 #include "nss_nlcmn_if.h"
 #include "nss_nlipsec_if.h"
+#include "nss_nlipv6_if.h"
 
 
 /* remove the "%d" and the NULL terminator */
@@ -191,7 +192,9 @@
 {
 	struct nss_ipsecmgr_encap_v4_tuple *v4_tuple;
 	struct nss_ipsecmgr_encap_v4_subnet *v4_subnet;
+	struct nss_ipsecmgr_encap_v6_tuple *v6_tuple;
 	struct nss_ipsecmgr_sa_v4 *v4;
+	struct nss_ipsecmgr_sa_v6 *v6;
 	const uint32_t encap_req_type = (NSS_CRYPTO_REQ_TYPE_AUTH | NSS_CRYPTO_REQ_TYPE_ENCRYPT);
 	uint32_t req_type;
 
@@ -216,6 +219,24 @@
 		}
 		break;
 	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)) {
+			nss_nl_error("Invalid v6 src_ip\n");
+			return -1;
+		}
+
+		if (!bitmap_empty((unsigned long *)v6_tuple->dst_ip, NSS_NLIPV6_ADDR_BITS)) {
+			nss_nl_error("Invalid v6 dst_ip\n");
+			return -1;
+		}
+
+		if (!v6_tuple->next_hdr) {
+			nss_nl_error("Invalid v6 next_hdr\n");
+			return -1;
+		}
+		break;
+
 	case NSS_IPSECMGR_FLOW_TYPE_V6_SUBNET:
 	default:
 		nss_nl_error("Invalid flow type\n");
@@ -234,7 +255,26 @@
 			return -2;
 		}
 		break;
+
 	case NSS_IPSECMGR_SA_TYPE_V6:
+		v6 = &encap_sa->data.v6;
+
+		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)) {
+			nss_nl_error("Invalid V6 dst_ip\n");
+			return -2;
+		}
+
+		if (!v6->spi_index) {
+			nss_nl_error("Invalid V6 spi_index\n");
+			return -2;
+		}
+		break;
+
 	default:
 		nss_nl_error("Invalid SA type\n");
 		return -2;
@@ -259,6 +299,7 @@
 static int nss_nlipsec_verify_create_decap(struct nss_ipsecmgr_sa *decap_sa, struct nss_ipsecmgr_sa_data *decap_data)
 {
 	struct nss_ipsecmgr_sa_v4 *v4;
+	struct nss_ipsecmgr_sa_v6 *v6;
 
 	const uint32_t decap_req_type = (NSS_CRYPTO_REQ_TYPE_AUTH | NSS_CRYPTO_REQ_TYPE_DECRYPT);
 	uint32_t req_type;
@@ -275,7 +316,26 @@
 			return -2;
 		}
 		break;
+
 	case NSS_IPSECMGR_SA_TYPE_V6:
+		v6 = &decap_sa->data.v6;
+
+		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)) {
+			nss_nl_error("Invalid V6 dst_ip\n");
+			return -2;
+		}
+
+		if (!v6->spi_index) {
+			nss_nl_error("Invalid V6 spi_index\n");
+			return -2;
+		}
+		break;
+
 	default:
 		nss_nl_error("Invalid SA type\n");
 		return -2;
@@ -300,7 +360,10 @@
 {
 	struct nss_ipsecmgr_encap_v4_tuple *v4_tuple;
 	struct nss_ipsecmgr_encap_v4_subnet *v4_subnet;
+	struct nss_ipsecmgr_encap_v6_tuple *v6_tuple;
 	struct nss_ipsecmgr_sa_v4 *v4;
+	struct nss_ipsecmgr_sa_v6 *v6;
+
 	/*
 	 * verify flow
 	 */
@@ -313,6 +376,7 @@
 			return -1;
 		}
 		break;
+
 	case NSS_IPSECMGR_FLOW_TYPE_V4_SUBNET:
 		v4_subnet = &encap_flow->data.v4_subnet;
 
@@ -321,7 +385,26 @@
 			return -1;
 		}
 		break;
+
 	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)) {
+			nss_nl_error("Invalid v6 src_ip\n");
+			return -1;
+		}
+
+		if (!bitmap_empty((unsigned long *)v6_tuple->dst_ip, NSS_NLIPV6_ADDR_BITS)) {
+			nss_nl_error("Invalid v6 dest_ip\n");
+			return -1;
+		}
+
+		if (!v6_tuple->next_hdr) {
+			nss_nl_error("Invalid v6 next_hdr\n");
+			return -1;
+		}
+		break;
+
 	case NSS_IPSECMGR_FLOW_TYPE_V6_SUBNET:
 	default:
 		nss_nl_error("Invalid flow type\n");
@@ -341,6 +424,24 @@
 		}
 		break;
 	case NSS_IPSECMGR_SA_TYPE_V6:
+		v6 = &encap_sa->data.v6;
+
+		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)) {
+			nss_nl_error("Invalid V6 dst_ip\n");
+			return -2;
+		}
+
+		if (!v6->spi_index) {
+			nss_nl_error("Invalid V6 spi_index\n");
+			return -2;
+		}
+		break;
+
 	default:
 		nss_nl_error("Invalid SA type\n");
 		return -2;
@@ -355,6 +456,7 @@
 static int nss_nlipsec_verify_destroy_encap_sa(struct nss_ipsecmgr_sa *encap_sa)
 {
 	struct nss_ipsecmgr_sa_v4 *v4;
+	struct nss_ipsecmgr_sa_v6 *v6;
 	/*
 	 * verify SA configuration
 	 */
@@ -368,6 +470,24 @@
 		}
 		break;
 	case NSS_IPSECMGR_SA_TYPE_V6:
+		v6 = &encap_sa->data.v6;
+
+		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)) {
+			nss_nl_error("Invalid V6 dst_ip\n");
+			return -2;
+		}
+
+		if (!v6->spi_index) {
+			nss_nl_error("Invalid V6 spi_index\n");
+			return -2;
+		}
+		break;
+
 	default:
 		nss_nl_error("Invalid SA type\n");
 		return -2;
@@ -382,6 +502,7 @@
 static int nss_nlipsec_verify_destroy_decap(struct nss_ipsecmgr_sa *decap_sa)
 {
 	struct nss_ipsecmgr_sa_v4 *v4;
+	struct nss_ipsecmgr_sa_v6 *v6;
 	/*
 	 * verify SA configuration
 	 */
@@ -395,6 +516,24 @@
 		}
 		break;
 	case NSS_IPSECMGR_SA_TYPE_V6:
+		v6 = &decap_sa->data.v6;
+
+		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)) {
+			nss_nl_error("Invalid V6 dst_ip\n");
+			return -2;
+		}
+
+		if (!v6->spi_index) {
+			nss_nl_error("Invalid V6 spi_index\n");
+			return -2;
+		}
+		break;
+
 	default:
 		nss_nl_error("Invalid SA type\n");
 		return -2;
diff --git a/netlink/nss_nlipv6.c b/netlink/nss_nlipv6.c
new file mode 100644
index 0000000..a258af9
--- /dev/null
+++ b/netlink/nss_nlipv6.c
@@ -0,0 +1,818 @@
+/*
+ **************************************************************************
+ * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ * Permission to use, copy, modify, and/or distribute this software for
+ * any purpose with or without fee is hereby granted, provided that the
+ * above copyright notice and this permission notice appear in all copies.
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ **************************************************************************
+ */
+
+/*
+ * nss_nlipv6.c
+ *	NSS Netlink IPv6 Handler
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/version.h>
+#include <linux/if.h>
+#include <linux/netlink.h>
+#include <linux/rcupdate.h>
+#include <linux/etherdevice.h>
+#include <linux/if_addr.h>
+#include <linux/version.h>
+#include <linux/vmalloc.h>
+#include <linux/completion.h>
+#include <linux/semaphore.h>
+#include <net/addrconf.h>
+
+#include <net/genetlink.h>
+#include <net/route.h>
+#include <net/ip6_route.h>
+#include <net/arp.h>
+#include <net/neighbour.h>
+#include <net/genetlink.h>
+#include <net/net_namespace.h>
+#include <net/sock.h>
+
+#include <nss_api_if.h>
+#include <nss_cmn.h>
+#include <nss_ipsec.h>
+#include <nss_nl_if.h>
+#include "nss_nl.h"
+#include "nss_nlipv6.h"
+#include "nss_nlcmn_if.h"
+#include "nss_nlipv6_if.h"
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0))
+#define DST_NEIGH_LOOKUP(dst, ip_addr) dst_neigh_lookup(dst, ip_addr)
+#else
+#define DST_NEIGH_LOOKUP(dst, ip_addr) dst_get_neighbour_noref(dst)
+#endif
+
+/*
+ * NSS NETLINK IPv6 context
+ */
+struct nss_nlipv6_ctx {
+	void *nss;
+};
+
+/*
+ * prototypes
+ */
+
+static int nss_nlipv6_ops_create_rule(struct sk_buff *skb, struct genl_info *info);
+static int nss_nlipv6_ops_destroy_rule(struct sk_buff *skb, struct genl_info *info);
+
+
+/*
+ * IPV6 family definition
+ */
+static struct genl_family nss_nlipv6_family = {
+	.id = GENL_ID_GENERATE,				/* Auto generate ID */
+	.name = NSS_NLIPV6_FAMILY,			/* family name string */
+	.hdrsize = sizeof(struct nss_nlipv6_rule),	/* NSS NETLINK IPV6 rule */
+	.version = NSS_NL_VER,				/* Set it to NSS_NLIPV6 version */
+	.maxattr = NSS_IPV6_MAX_MSG_TYPES,		/* maximum commands supported */
+	.netnsok = true,
+	.pre_doit = NULL,
+	.post_doit = NULL,
+};
+
+/*
+ * multicast group for sending message status & events
+ */
+static struct genl_multicast_group nss_nlipv6_mcgrp = {
+	.name = NSS_NLIPV6_FAMILY,
+};
+
+/*
+ * operation table called by the generic netlink layer based on the command
+ */
+static struct genl_ops nss_nlipv6_ops[] = {
+	{.cmd = NSS_IPV6_TX_CREATE_RULE_MSG, .doit = nss_nlipv6_ops_create_rule,},	/* rule create */
+	{.cmd = NSS_IPV6_TX_DESTROY_RULE_MSG, .doit = nss_nlipv6_ops_destroy_rule,},	/* rule destroy */
+};
+
+#define NSS_NLIPV6_OPS_SZ ARRAY_SIZE(nss_nlipv6_ops)
+
+static struct nss_nlipv6_ctx gbl_ctx;
+
+/*
+ * nss_nlipv6_get_neigh()
+ * 	Returns neighbour reference for a given IP address
+ */
+static struct neighbour *nss_nlipv6_get_neigh(uint32_t dst_addr[4])
+{
+	struct neighbour *neigh;
+	struct dst_entry *dst;
+	struct rt6_info *rt;
+	struct in6_addr daddr;
+
+	IPV6_ADDR_TO_IN6_ADDR(daddr, dst_addr);
+
+	rt = rt6_lookup(&init_net, &daddr, NULL, 0, 0);
+	if (!rt) {
+		return NULL;
+	}
+
+	dst = (struct dst_entry *)rt;
+
+	/*
+	 * neighbour lookup using IP address in the route table
+	 */
+	neigh = DST_NEIGH_LOOKUP(dst, &daddr);
+	if (likely(neigh)) {
+		neigh_hold(neigh);
+		dst_release(dst);
+
+		return neigh;
+	}
+
+	return NULL;
+}
+
+/*
+ * nss_nlipv6_get_addr_htonl()
+ * 	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])
+{
+	int32_t i;
+
+	for (i = 0; i < 4; i++) {
+		dst[i] = htonl(src[i]);
+	}
+}
+
+/*
+ * nss_nlipv6_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_nlipv6_get_macaddr(uint32_t ip_addr[4], uint8_t mac_addr[])
+{
+	struct neighbour *neigh;
+	struct  in6_addr addr;
+
+	nss_nlipv6_get_addr_htonl(ip_addr, addr.s6_addr32);
+
+	if (ipv6_addr_is_multicast(&addr)) {
+		/*
+		 * fixed
+		 */
+		mac_addr[0] = 0x33;
+		mac_addr[1] = 0x33;
+
+		/*
+		 * Last word of the IPv6 address.
+		 */
+		memcpy(&mac_addr[2], &addr.s6_addr32[3], sizeof(uint32_t));
+		return 0;
+
+	}
+
+	rcu_read_lock();
+
+	/*
+	 * retrieve the neighbour
+	 */
+	neigh = nss_nlipv6_get_neigh(addr.s6_addr32);
+	if (!neigh) {
+		rcu_read_unlock();
+		nss_nl_info("neighbour lookup failed for %pI6c\n", addr.s6_addr32);
+		return -ENODEV;
+	}
+
+	rcu_read_unlock();
+
+	if ((neigh->nud_state & NUD_VALID) == 0) {
+		nss_nl_info("neighbour state is invalid for %pI6c\n", addr.s6_addr32);
+		goto fail;
+	}
+
+	if (!neigh->dev) {
+		nss_nl_info("neighbour device not found for %pI6c\n", addr.s6_addr32);
+		goto fail;
+	}
+
+	if (is_multicast_ether_addr(neigh->ha)) {
+		nss_nl_info("neighbour MAC address is multicast or broadcast\n");
+		goto fail;
+	}
+
+	memcpy(mac_addr, neigh->ha, (size_t)neigh->dev->addr_len);
+	neigh_release(neigh);
+	return 0;
+fail:
+
+	neigh_release(neigh);
+	return -ENODEV;
+}
+
+
+/*
+ * nss_nlipv6_verify_5tuple()
+ * 	verify and override 5-tuple entries
+ */
+static int nss_nlipv6_verify_5tuple(struct nss_ipv6_rule_create_msg *msg)
+{
+	struct nss_ipv6_5tuple *tuple = &msg->tuple;
+
+	/*
+	 * protocol must be provided
+	 */
+	if (!tuple->protocol) {
+		nss_nl_info("Empty protocol for 5-tuple\n");
+		return -EINVAL;
+	}
+
+	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)) {
+		nss_nl_info("Empty return IP\n");
+		return -EINVAL;
+	}
+
+	/* Validate the port number */
+	switch (tuple->protocol) {
+	case NSS_NLIPV6_UDP:
+	case NSS_NLIPV6_TCP:
+	case NSS_NLIPV6_SCTP:
+		if (!tuple->flow_ident) {
+			nss_nl_info("Empty flow ident\n");
+			return -EINVAL;
+		}
+
+		if (!tuple->return_ident) {
+			nss_nl_info("Empty return ident\n");
+			return -EINVAL;
+		}
+		break;
+	}
+
+	return 0;
+}
+
+/*
+ * nss_nlipv6_verify_conn_rule()
+ * 	verify and override connection rule entries
+ */
+static int nss_nlipv6_verify_conn_rule(struct nss_ipv6_rule_create_msg *msg, struct net_device *flow_dev,
+					struct net_device *return_dev)
+{
+	struct nss_ipv6_connection_rule *conn = &msg->conn_rule;
+	const size_t rule_sz = sizeof(struct nss_ipv6_connection_rule);
+	bool valid;
+
+	/*
+	 * connection rule is not valid ignore rest of the checks
+	 */
+	valid = msg->valid_flags & NSS_IPV6_RULE_CREATE_CONN_VALID;
+	if (!valid) {
+		memset(conn, 0, rule_sz);
+		return -EINVAL;
+	}
+
+	/*
+	 * update the flow  & return MAC address
+	 */
+	if (nss_nlipv6_get_macaddr(msg->tuple.flow_ip, (uint8_t *)conn->flow_mac)) {
+		nss_nl_info("Error in Updating the Flow MAC Address \n");
+		return -EINVAL;
+	}
+
+	if (nss_nlipv6_get_macaddr(msg->tuple.return_ip, (uint8_t *)conn->return_mac)) {
+		nss_nl_info("Error in Updating the Return MAC Address \n");
+		return -EINVAL;
+	}
+
+	/*
+	 * update flow and return interface numbers
+	 */
+	conn->flow_interface_num = nss_cmn_get_interface_number_by_dev(flow_dev);
+	conn->return_interface_num = nss_cmn_get_interface_number_by_dev(return_dev);
+
+	/*
+	 * update the flow & return MTU(s)
+	 */
+	conn->flow_mtu = flow_dev->mtu;
+	conn->return_mtu = return_dev->mtu;
+
+	return 0;
+}
+
+/*
+ * nss_nlipv6_verify_tcp_rule()
+ * 	verify and override TCP protocol rule entries
+ */
+static int nss_nlipv6_verify_tcp_rule(struct nss_ipv6_rule_create_msg *msg)
+{
+	struct nss_ipv6_protocol_tcp_rule *tcp = &msg->tcp_rule;
+	const size_t rule_sz = sizeof(struct nss_ipv6_protocol_tcp_rule);
+	bool valid;
+
+	/*
+	 * tcp rule is not valid ignore rest of the checks
+	 */
+	valid = msg->valid_flags & NSS_IPV6_RULE_CREATE_TCP_VALID;
+	if (!valid) {
+		memset(tcp, 0, rule_sz);
+		return 0;
+	}
+
+	/*
+	 * XXX: add addtional checks as required
+	 */
+
+	return 0;
+}
+
+/*
+ * nss_nlipv6_verify_pppoe_rule()
+ * 	verify and override pppoe rule entries
+ */
+static int nss_nlipv6_verify_pppoe_rule(struct nss_ipv6_rule_create_msg *msg)
+{
+	struct nss_ipv6_pppoe_rule *pppoe = &msg->pppoe_rule;
+	const size_t rule_sz = sizeof(struct nss_ipv6_pppoe_rule);
+	bool valid;
+
+	/*
+	 * pppoe rule is not valid ignore rest of the checks
+	 */
+	valid = msg->valid_flags & NSS_IPV6_RULE_CREATE_PPPOE_VALID;
+	if (!valid) {
+		memset(pppoe, 0, rule_sz);
+		return 0;
+	}
+
+	/*
+	 * XXX: add addtional checks as required
+	 */
+	return 0;
+}
+
+/*
+ * nss_nlipv6_verify_qos_rule()
+ * 	verify and override qos rule entries
+ */
+static int nss_nlipv6_verify_qos_rule(struct nss_ipv6_rule_create_msg *msg)
+{
+	struct nss_ipv6_qos_rule *qos = &msg->qos_rule;
+	const size_t rule_sz = sizeof(struct nss_ipv6_qos_rule);
+	bool valid;
+
+	/*
+	 * qos rule is not valid ignore rest of the checks
+	 */
+	valid = msg->valid_flags & NSS_IPV6_RULE_CREATE_QOS_VALID;
+	if (!valid) {
+		memset(qos, 0, rule_sz);
+		return 0;
+	}
+
+	/*
+	 * XXX: add addtional checks as required
+	 */
+	return 0;
+}
+
+/*
+ * nss_nlipv6_verify_dscp_rule()
+ * 	verify and override dscp rule entries
+ */
+static int nss_nlipv6_verify_dscp_rule(struct nss_ipv6_rule_create_msg *msg)
+{
+	struct nss_ipv6_dscp_rule *dscp = &msg->dscp_rule;
+	const size_t rule_sz = sizeof(struct nss_ipv6_dscp_rule);
+	bool valid;
+
+	/*
+	 * dscp rule is not valid ignore rest of the checks
+	 */
+	valid = msg->valid_flags & NSS_IPV6_RULE_CREATE_DSCP_MARKING_VALID;
+	if (!valid) {
+		memset(dscp, 0, rule_sz);
+		return 0;
+	}
+
+	/*
+	 * XXX: add addtional checks as required
+	 */
+	return 0;
+}
+
+/*
+ * nss_nlipv6_verify_vlan_rule()
+ * 	verify and override vlan rule entries
+ */
+static int nss_nlipv6_verify_vlan_rule(struct nss_ipv6_rule_create_msg *msg)
+{
+	struct nss_ipv6_vlan_rule *vlan_outer = &msg->vlan_primary_rule;
+	struct nss_ipv6_vlan_rule *vlan_inner = &msg->vlan_secondary_rule;
+	bool valid;
+
+	/*
+	 * vlan rule is not valid ignore rest of the checks
+	 */
+	valid = msg->valid_flags & NSS_IPV6_RULE_CREATE_VLAN_VALID;
+	if (!valid) {
+		vlan_outer->ingress_vlan_tag = NSS_NLIPV6_VLAN_ID_NOT_CONFIGURED;
+		vlan_outer->egress_vlan_tag = NSS_NLIPV6_VLAN_ID_NOT_CONFIGURED;
+
+		vlan_inner->ingress_vlan_tag = NSS_NLIPV6_VLAN_ID_NOT_CONFIGURED;
+		vlan_inner->egress_vlan_tag = NSS_NLIPV6_VLAN_ID_NOT_CONFIGURED;
+
+		return 0;
+	}
+
+	/*
+	 * XXX: add addtional checks as required
+	 */
+	return 0;
+}
+
+/*
+ * nss_nlipv6_process_notify()
+ * 	process notification messages from NSS
+ */
+static void nss_nlipv6_process_notify(void *app_data, struct nss_ipv6_msg *nim)
+{
+	struct nss_nlipv6_rule *nl_rule;
+	struct nss_ipv6_msg *nl_nim;
+	struct sk_buff *skb;
+
+	skb = nss_nl_new_msg(&nss_nlipv6_family, nim->cm.type);
+	if (!skb) {
+		nss_nl_error("unable to allocate NSS_NLIPV6 event\n");
+		return;
+	}
+
+	nl_rule = nss_nl_get_data(skb);
+	nl_nim = &nl_rule->nim;
+
+	/*
+	 * initialize the NETLINK common header
+	 */
+	nss_nlipv6_rule_init(nl_rule, nim->cm.type);
+
+	/*
+	 * clear NSS common message items that are not useful to uspace
+	 */
+	nim->cm.interface = 0;
+	nim->cm.cb = (uint32_t)NULL;
+	nim->cm.app_data = (uint32_t)NULL;
+
+	/*
+	 * copy the contents of the sync message into the NETLINK message
+	 */
+	memcpy(nl_nim, nim, sizeof(struct nss_ipv6_msg));
+
+	nss_nl_mcast_event(&nss_nlipv6_mcgrp, skb);
+}
+
+/*
+ * nss_nlipv6_resp_create_rule()
+ * 	handle response for create rule
+ */
+static void nss_nlipv6_process_resp(void *app_data, struct nss_ipv6_msg *nim)
+{
+	struct sk_buff *resp = (struct sk_buff *)app_data;
+	struct nss_nlipv6_rule *nl_rule;
+	struct nss_ipv6_msg *nl_nim;
+
+	nl_rule = nss_nl_get_data(resp);
+	nl_nim = &nl_rule->nim;
+
+	/*
+	 * copy the message response data into the NL message buffer. If, FW
+	 * has updated the message then we must updated the same into the NL
+	 * message as the NL message buffer is different from what was sent
+	 * to FW
+	 */
+	memcpy(nl_nim, nim, sizeof(struct nss_ipv6_msg));
+
+	nss_nl_ucast_resp(resp);
+}
+
+/*
+ * nss_nlipv6_ops_create_rule()
+ * 	rule create handler
+ */
+static int nss_nlipv6_ops_create_rule(struct sk_buff *skb, struct genl_info *info)
+{
+	struct net_device *return_dev;
+	struct net_device *flow_dev;
+	struct nss_nlipv6_rule *nl_rule;
+	struct nss_nlcmn *nl_cm;
+	struct nss_ipv6_msg *nim;
+	nss_tx_status_t tx_status;
+	struct sk_buff *resp;
+	uint32_t pid;
+	int error;
+
+	/*
+	 * extract the message payload
+	 */
+	nl_cm = nss_nl_get_msg(&nss_nlipv6_family, info, NSS_IPV6_TX_CREATE_RULE_MSG);
+	if (!nl_cm) {
+		nss_nl_error("unable to extract rule create data\n");
+		return -EINVAL;
+	}
+
+	/*
+	 * Message validation required before accepting the configuration
+	 */
+	nl_rule = container_of(nl_cm, struct nss_nlipv6_rule, cm);
+	nim = &nl_rule->nim;
+	pid = nl_cm->pid;
+
+	/*
+	 * extract netdevices for flow and return
+	 */
+	flow_dev = dev_get_by_name(&init_net, nl_rule->flow_ifname);
+	if (!flow_dev) {
+		nss_nl_error("%d:flow interface is not available\n", pid);
+		return -EINVAL;
+	}
+
+	return_dev = dev_get_by_name(&init_net, nl_rule->return_ifname);
+	if (!return_dev) {
+		dev_put(flow_dev);
+		nss_nl_error("%d:return interface is not available\n", pid);
+		return -EINVAL;
+	}
+
+	/*
+	 * check 5-tuple
+	 */
+	error = nss_nlipv6_verify_5tuple(&nim->msg.rule_create);
+	if (error < 0) {
+		nss_nl_error("%d:invalid 5-tuple information passed\n", pid);
+		goto done;
+	}
+
+	/*
+	 * check connection rule
+	 */
+	error = nss_nlipv6_verify_conn_rule(&nim->msg.rule_create, flow_dev, return_dev);
+	if (error < 0) {
+		nss_nl_error("%d:invalid conn rule information passed\n", pid);
+		goto done;
+	}
+
+	/*
+	 * check tcp protocol rule
+	 */
+	error = nss_nlipv6_verify_tcp_rule(&nim->msg.rule_create);
+	if (error < 0) {
+		nss_nl_error("%d:invalid tcp rule information passed\n", pid);
+		goto done;
+	}
+
+	/*
+	 * check pppoe rule
+	 */
+	error = nss_nlipv6_verify_pppoe_rule(&nim->msg.rule_create);
+	if (error < 0) {
+		nss_nl_error("%d:invalid pppoe rule information passed\n", pid);
+		goto done;
+	}
+
+	/*
+	 * check qos rule
+	 */
+	error = nss_nlipv6_verify_qos_rule(&nim->msg.rule_create);
+	if (error < 0) {
+		nss_nl_error("%d:invalid qos rule information passed\n", pid);
+		goto done;
+	}
+
+	/*
+	 * check dscp rule
+	 */
+	error = nss_nlipv6_verify_dscp_rule(&nim->msg.rule_create);
+	if (error < 0) {
+		nss_nl_error("%d:invalid dscp rule information passed\n", pid);
+		goto done;
+	}
+
+	/*
+	 * check vlan rule
+	 */
+	error = nss_nlipv6_verify_vlan_rule(&nim->msg.rule_create);
+	if (error < 0) {
+		nss_nl_error("%d:invalid vlan rule information passed\n", pid);
+		goto done;
+	}
+
+	/*
+	 * copy the NL message for response
+	 */
+	resp = nss_nl_copy_msg(skb);
+	if (!resp) {
+		nss_nl_error("%d:unable to save response data from NL buffer\n", pid);
+		error = -ENOMEM;
+		goto done;
+	}
+
+	/*
+	 * Initialize the common message
+	 */
+	nss_ipv6_msg_init(nim,					/* ipv6 message */
+			NSS_IPV6_RX_INTERFACE,			/* interface number */
+			NSS_IPV6_TX_CREATE_RULE_MSG,		/* rule */
+			sizeof(struct nss_ipv6_rule_create_msg),/* message size */
+			nss_nlipv6_process_resp,		/* response callback */
+			(void *)resp);				/* app context */
+
+	/*
+	 * Push Rule to NSS
+	 */
+	tx_status = nss_ipv6_tx(gbl_ctx.nss, nim);
+	if (tx_status != NSS_TX_SUCCESS) {
+		nlmsg_free(resp);
+		nss_nl_error("%d:unable to send IPV6 rule create, status(%d)\n", pid, tx_status);
+		error = -EBUSY;
+		goto done;
+	}
+
+done:
+	dev_put(flow_dev);
+	dev_put(return_dev);
+
+	return error;
+}
+
+/*
+ * nss_nlipv6_ops_destroy_rule()
+ * 	rule delete handler
+ */
+static int nss_nlipv6_ops_destroy_rule(struct sk_buff *skb, struct genl_info *info)
+{
+	struct nss_nlipv6_rule *nl_rule;
+	struct nss_nlcmn *nl_cm;
+	struct nss_ipv6_msg *nim;
+	nss_tx_status_t tx_status;
+	struct sk_buff *resp;
+	uint32_t pid;
+	int error;
+
+	/*
+	 * extract the message payload
+	 */
+	nl_cm = nss_nl_get_msg(&nss_nlipv6_family, info, NSS_IPV6_TX_DESTROY_RULE_MSG);
+	if (!nl_cm) {
+		nss_nl_error("unable to extract rule destroy data\n");
+		return -EINVAL;
+	}
+
+	/*
+	 * Message validation required before accepting the configuration
+	 */
+	nl_rule = container_of(nl_cm, struct nss_nlipv6_rule, cm);
+	nim = &nl_rule->nim;
+	pid = nl_cm->pid;
+
+	/*
+	 * check 5-tuple
+	 */
+	error = nss_nlipv6_verify_5tuple(&nim->msg.rule_create);
+	if (error < 0) {
+		nss_nl_error("%d:invalid 5-tuple information passed\n", pid);
+		goto done;
+	}
+
+	/*
+	 * copy the NL message for response
+	 */
+	resp = nss_nl_copy_msg(skb);
+	if (!resp) {
+		nss_nl_error("%d:unable to save response data from NL buffer\n", pid);
+		error = -ENOMEM;
+		goto done;
+	}
+	/*
+	 * Initialize the common message
+	 */
+	nss_ipv6_msg_init(nim,						/* ipv6 message */
+			NSS_IPV6_RX_INTERFACE,				/* interface number */
+			NSS_IPV6_TX_DESTROY_RULE_MSG,			/* rule */
+			sizeof(struct nss_ipv6_rule_destroy_msg),	/* message size */
+			nss_nlipv6_process_resp,			/* response callback */
+			(void *)resp);					/* app context */
+
+	/*
+	 * Push rule to NSS
+	 */
+	tx_status = nss_ipv6_tx(gbl_ctx.nss, nim);
+	if (tx_status != NSS_TX_SUCCESS) {
+		nlmsg_free(resp);
+		nss_nl_error("%d:unable to send IPV6 rule delete, status(%d)\n", pid, tx_status);
+		return -EBUSY;
+	}
+
+done:
+	return error;
+}
+
+/*
+ * nss_nlipv6_init()
+ * 	handler init
+ */
+bool nss_nlipv6_init(void)
+{
+	int error;
+
+	nss_nl_info_always("Init NSS netlink IPV6 handler\n");
+
+	/*
+	 * register NETLINK ops with the family
+	 */
+	error = genl_register_family_with_ops(&nss_nlipv6_family, nss_nlipv6_ops, NSS_NLIPV6_OPS_SZ);
+	if (error != 0) {
+		nss_nl_info_always("Error: unable to register IPV6 family\n");
+		return false;
+	}
+
+	/*
+	 * register NETLINK MCAST group for notifications
+	 */
+	error = genl_register_mc_group(&nss_nlipv6_family, &nss_nlipv6_mcgrp);
+	if (error != 0) {
+		nss_nl_info_always("Error: unable to register IPV6 Netlink multicast group\n");
+		goto unreg_family;
+	}
+
+	/*
+	 * register device call back handler for ipv6 from NSS
+	 */
+	gbl_ctx.nss = nss_ipv6_notify_register(nss_nlipv6_process_notify, &gbl_ctx);
+	if (!gbl_ctx.nss) {
+		nss_nl_info_always("Error: retreiving the NSS Context \n");
+		goto unreg_all;
+	}
+
+	return true;
+
+	/*
+	 * undo all registeration
+	 */
+unreg_all:
+	genl_unregister_mc_group(&nss_nlipv6_family, &nss_nlipv6_mcgrp);
+unreg_family:
+	genl_unregister_family(&nss_nlipv6_family);
+
+	return false;
+}
+
+/*
+ * nss_nlipv6_exit()
+ *	handler exit
+ */
+bool nss_nlipv6_exit(void)
+{
+	int error;
+
+	nss_nl_info_always("Exit NSS netlink IPV6 handler\n");
+
+	/*
+	 * unregister the ops family
+	 */
+	error = genl_unregister_family(&nss_nlipv6_family);
+	if (error != 0) {
+		nss_nl_info_always("unable to unregister IPV6 NETLINK family\n");
+		return false;
+	}
+
+	/*
+	 * unregister the multicast family
+	 */
+	genl_unregister_mc_group(&nss_nlipv6_family, &nss_nlipv6_mcgrp);
+
+	/*
+	 * Unregister the device callback handler for ipv6
+	 */
+	nss_ipv6_notify_unregister();
+
+	gbl_ctx.nss = NULL;
+
+	return true;
+}
+
diff --git a/netlink/nss_nlipv6.h b/netlink/nss_nlipv6.h
new file mode 100644
index 0000000..70312a3
--- /dev/null
+++ b/netlink/nss_nlipv6.h
@@ -0,0 +1,42 @@
+/*
+ **************************************************************************
+ * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ * Permission to use, copy, modify, and/or distribute this software for
+ * any purpose with or without fee is hereby granted, provided that the
+ * above copyright notice and this permission notice appear in all copies.
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ **************************************************************************
+ */
+
+/*
+ * nss_nlipv6.h
+ *	NSS Netlink IPv6 API definitions
+ */
+#ifndef __NSS_NLIPV6_H
+#define __NSS_NLIPV6_H
+
+/*IP protocol type */
+#define NSS_NLIPV6_UDP 17
+#define NSS_NLIPV6_TCP 6
+#define NSS_NLIPV6_SCTP 132
+
+#define NSS_NLIPV6_VLAN_ID_NOT_CONFIGURED 0xFFF    /* Invalid vlan 4095 */
+
+bool nss_nlipv6_init(void);
+bool nss_nlipv6_exit(void);
+
+#if defined(CONFIG_NSS_NLIPV6)
+#define NSS_NLIPV6_INIT nss_nlipv6_init
+#define NSS_NLIPV6_EXIT nss_nlipv6_exit
+#else
+#define NSS_NLIPV6_INIT 0
+#define NSS_NLIPV6_EXIT 0
+#endif /* !CONFIG_NSS_NLIPV6 */
+
+#endif /* __NSS_NLIPV6_H */