[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 */