diff mbox

[ovs-dev,CudaMailTagged,RFC,v2,2/5] NSH key attributes handling of kernel Netlink message

Message ID 1468344981-11863-1-git-send-email-johnson.li@intel.com
State Changes Requested
Headers show

Commit Message

Johnson.Li July 12, 2016, 5:36 p.m. UTC
The openvswitch exchange the key with key netlink message
between the kernel data path and user space flow tables.

Signed-off-by: Johnson Li <johnson.li@intel.com>
diff mbox

Patch

diff --git a/datapath/flow_netlink.c b/datapath/flow_netlink.c
index eafaca2..e6f42ea 100644
--- a/datapath/flow_netlink.c
+++ b/datapath/flow_netlink.c
@@ -279,6 +279,23 @@  size_t ovs_tun_key_attr_size(void)
 		+ nla_total_size(2);   /* OVS_TUNNEL_KEY_ATTR_TP_DST */
 }
 
+size_t ovs_nsh_key_attr_size(void)
+{
+	return    nla_total_size(1)    /* OVS_NSH_KEY_ATTR_FLAGS */
+		+ nla_total_size(1)    /* OVS_NSH_KEY_ATTR_MD_TYPE */
+		+ nla_total_size(1)    /* OVS_NSH_KEY_ATTR_NEXT_PROTO */
+		+ nla_total_size(1)    /* OVS_NSH_KEY_ATTR_NSI */
+		+ nla_total_size(4)    /* OVS_NSH_KEY_ATTR_NSP */
+		+ nla_total_size(256)  /* OVS_NSH_KEY_ATTR_METADATA */
+		/* OVS_NSH_KEY_ATTR_METADATA is mutually exclusive with
+		 * OVS_NSH_KEY_ATTR_NSHCx and covered by it.
+		 */
+		+ nla_total_size(0)    /* OVS_NSH_KEY_ATTR_NSHC1 */
+		+ nla_total_size(0)    /* OVS_NSH_KEY_ATTR_NSHC2 */
+		+ nla_total_size(0)    /* OVS_NSH_KEY_ATTR_NSHC3 */
+		+ nla_total_size(0);   /* OVS_NSH_KEY_ATTR_NSHC4 */
+}
+
 size_t ovs_key_attr_size(void)
 {
 	/* Whenever adding new OVS_KEY_ FIELDS, we should consider
@@ -300,6 +317,8 @@  size_t ovs_key_attr_size(void)
 		+ nla_total_size(12)  /* OVS_KEY_ATTR_ETHERNET */
 		+ nla_total_size(2)   /* OVS_KEY_ATTR_ETHERTYPE */
 		+ nla_total_size(4)   /* OVS_KEY_ATTR_VLAN */
+		+ nla_total_size(0)   /* OVS_KEY_ATTR_NSH */
+		+ ovs_nsh_key_attr_size()
 		+ nla_total_size(0)   /* OVS_KEY_ATTR_ENCAP */
 		+ nla_total_size(2)   /* OVS_KEY_ATTR_ETHERTYPE */
 		+ nla_total_size(40)  /* OVS_KEY_ATTR_IPV6 */
@@ -327,6 +346,19 @@  static const struct ovs_len_tbl ovs_tunnel_key_lens[OVS_TUNNEL_KEY_ATTR_MAX + 1]
 						.next = ovs_vxlan_ext_key_lens },
 };
 
+static const struct ovs_len_tbl ovs_nsh_key_lens[OVS_NSH_KEY_ATTR_MAX + 1] = {
+	[OVS_NSH_KEY_ATTR_FLAGS]	    = { .len = 1 },
+	[OVS_NSH_KEY_ATTR_MD_TYPE]	    = { .len = 1 },
+	[OVS_NSH_KEY_ATTR_NEXT_PROTO]	    = { .len = 1 },
+	[OVS_NSH_KEY_ATTR_NSI]		    = { .len = 1 },
+	[OVS_NSH_KEY_ATTR_NSP]		    = { .len = 4 },
+	[OVS_NSH_KEY_ATTR_NSHC1]	    = { .len = 4 },
+	[OVS_NSH_KEY_ATTR_NSHC2]	    = { .len = 4 },
+	[OVS_NSH_KEY_ATTR_NSHC3]	    = { .len = 4 },
+	[OVS_NSH_KEY_ATTR_NSHC4]	    = { .len = 4 },
+	[OVS_NSH_KEY_ATTR_METADATA]	    = { .len = OVS_ATTR_VARIABLE },
+};
+
 /* The size of the argument for each %OVS_KEY_ATTR_* Netlink attribute.  */
 static const struct ovs_len_tbl ovs_key_lens[OVS_KEY_ATTR_MAX + 1] = {
 	[OVS_KEY_ATTR_ENCAP]	 = { .len = OVS_ATTR_NESTED },
@@ -335,6 +367,8 @@  static const struct ovs_len_tbl ovs_key_lens[OVS_KEY_ATTR_MAX + 1] = {
 	[OVS_KEY_ATTR_SKB_MARK]	 = { .len = sizeof(u32) },
 	[OVS_KEY_ATTR_ETHERNET]	 = { .len = sizeof(struct ovs_key_ethernet) },
 	[OVS_KEY_ATTR_VLAN]	 = { .len = sizeof(__be16) },
+	[OVS_KEY_ATTR_NSH]	 = { .len = OVS_ATTR_NESTED,
+				     .next = ovs_nsh_key_lens, },
 	[OVS_KEY_ATTR_ETHERTYPE] = { .len = sizeof(__be16) },
 	[OVS_KEY_ATTR_IPV4]	 = { .len = sizeof(struct ovs_key_ipv4) },
 	[OVS_KEY_ATTR_IPV6]	 = { .len = sizeof(struct ovs_key_ipv6) },
@@ -853,6 +887,159 @@  static int metadata_from_nlattrs(struct net *net, struct sw_flow_match *match,
 	return 0;
 }
 
+static int nsh_from_nlattr(const struct nlattr *attr,
+			   struct sw_flow_match *match, bool is_mask,
+			   bool log)
+{
+	struct nlattr *a;
+	int rem;
+
+	nla_for_each_nested(a, attr, rem) {
+		int type = nla_type(a);
+
+		if (type > OVS_NSH_KEY_ATTR_MAX) {
+			OVS_NLERR(log, "NSH attr %d out of range max %d",
+				  type, OVS_NSH_KEY_ATTR_MAX);
+			return -EINVAL;
+		}
+
+		if (!check_attr_len(nla_len(a),
+				    ovs_nsh_key_lens[type].len)) {
+			OVS_NLERR(log, "NSH attr %d has unexpected len %d"
+				  " expected %d", type, nla_len(a),
+				  ovs_nsh_key_lens[type].len);
+			return -EINVAL;
+		}
+
+		switch (type) {
+		case OVS_NSH_KEY_ATTR_FLAGS:
+			SW_FLOW_KEY_PUT(match, nsh.flags,
+					nla_get_u8(a), is_mask);
+			break;
+		case OVS_NSH_KEY_ATTR_MD_TYPE:
+			SW_FLOW_KEY_PUT(match, nsh.md_type,
+					nla_get_u8(a), is_mask);
+			break;
+		case OVS_NSH_KEY_ATTR_NEXT_PROTO:
+			SW_FLOW_KEY_PUT(match, nsh.next_proto,
+					nla_get_u8(a), is_mask);
+			break;
+		case OVS_NSH_KEY_ATTR_NSI:
+			SW_FLOW_KEY_PUT(match, nsh.nsi,
+					nla_get_u8(a), is_mask);
+			break;
+		case OVS_NSH_KEY_ATTR_NSP:
+			SW_FLOW_KEY_PUT(match, nsh.nsp,
+					nla_get_be32(a), is_mask);
+			break;
+		case OVS_NSH_KEY_ATTR_NSHC1:
+			SW_FLOW_KEY_PUT(match, nsh.nshc1,
+					nla_get_be32(a), is_mask);
+			break;
+		case OVS_NSH_KEY_ATTR_NSHC2:
+			SW_FLOW_KEY_PUT(match, nsh.nshc2,
+					nla_get_be32(a), is_mask);
+			break;
+		case OVS_NSH_KEY_ATTR_NSHC3:
+			SW_FLOW_KEY_PUT(match, nsh.nshc3,
+					nla_get_be32(a), is_mask);
+			break;
+		case OVS_NSH_KEY_ATTR_NSHC4:
+			SW_FLOW_KEY_PUT(match, nsh.nshc4,
+					nla_get_be32(a), is_mask);
+			break;
+		/* To be implemented for MD type 2 */
+		case OVS_NSH_KEY_ATTR_METADATA:
+			break;
+		default:
+			OVS_NLERR(log, "Unknown NSH attribute %d", type);
+			return -EINVAL;
+		}
+	}
+
+	if (rem > 0) {
+		OVS_NLERR(log, "NSH attribute has %d unknown bytes.", rem);
+		return -EINVAL;
+	}
+
+	if (!is_mask) {
+		if ((match->key->nsh.md_type != 0x1) &&
+		    (match->key->nsh.md_type != 0x2)) {
+			OVS_NLERR(log, "Invalid NSH Header MD Type!!!");
+			return -EINVAL;
+		}
+	}
+
+	return 0;
+}
+
+static int __nsh_to_nlattr(struct sk_buff *skb,
+			   const struct ovs_nsh_key *output)
+{
+	if (output->nsp) {
+		if (output->flags &&
+		    nla_put_u8(skb, OVS_NSH_KEY_ATTR_FLAGS,
+			       output->flags))
+			return -EMSGSIZE;
+		if (nla_put_u8(skb, OVS_NSH_KEY_ATTR_MD_TYPE,
+			       output->md_type))
+			return -EMSGSIZE;
+		if (output->next_proto &&
+		    nla_put_u8(skb, OVS_NSH_KEY_ATTR_NEXT_PROTO,
+			       output->next_proto))
+			return -EMSGSIZE;
+		if (output->nsi &&
+		    nla_put_u8(skb, OVS_NSH_KEY_ATTR_NSI,
+			       output->nsi))
+			return -EMSGSIZE;
+		if (output->nsp &&
+		    nla_put_be32(skb, OVS_NSH_KEY_ATTR_NSP,
+				 output->nsp))
+			return -EMSGSIZE;
+		if (output->md_type == 0x1) { /* NSH_M_TYPE1 */
+			if (output->nshc1 &&
+			    nla_put_be32(skb, OVS_NSH_KEY_ATTR_NSHC1,
+					 output->nshc1))
+				return -EMSGSIZE;
+			if (output->nshc2 &&
+			    nla_put_be32(skb, OVS_NSH_KEY_ATTR_NSHC2,
+					 output->nshc2))
+				return -EMSGSIZE;
+			if (output->nshc3 &&
+			    nla_put_be32(skb, OVS_NSH_KEY_ATTR_NSHC3,
+					 output->nshc3))
+				return -EMSGSIZE;
+			if (output->nshc4 &&
+			    nla_put_be32(skb, OVS_NSH_KEY_ATTR_NSHC4,
+					 output->nshc4))
+				return -EMSGSIZE;
+#if 0
+		} else if (output->md_type == 0x2) { /* NSH_M_TYPE2 */
+			/* To be implemented. */
+#endif
+		}
+	}
+	return 0;
+}
+
+static int nsh_to_nlattr(struct sk_buff *skb,
+			 const struct ovs_nsh_key *output)
+{
+	struct nlattr *nla;
+	int err;
+
+	nla = nla_nest_start(skb, OVS_KEY_ATTR_NSH);
+	if (!nla)
+		return -EMSGSIZE;
+
+	err = __nsh_to_nlattr(skb, output);
+	if (err)
+		return err;
+
+	nla_nest_end(skb, nla);
+	return 0;
+}
+
 static int ovs_key_from_nlattrs(struct net *net, struct sw_flow_match *match,
 				u64 attrs, const struct nlattr **a,
 				bool is_mask, bool log)
@@ -863,6 +1050,14 @@  static int ovs_key_from_nlattrs(struct net *net, struct sw_flow_match *match,
 	if (err)
 		return err;
 
+	if (attrs & (1ULL << OVS_KEY_ATTR_NSH)) {
+		if (nsh_from_nlattr(a[OVS_KEY_ATTR_NSH], match,
+				    is_mask, log) < 0) {
+			return -EINVAL;
+		}
+		attrs &= ~(1ULL << OVS_KEY_ATTR_NSH);
+	}
+
 	if (attrs & (1ULL << OVS_KEY_ATTR_ETHERNET)) {
 		const struct ovs_key_ethernet *eth_key;
 
@@ -1391,6 +1586,11 @@  static int __ovs_nla_put_key(const struct sw_flow_key *swkey,
 			goto nla_put_failure;
 	}
 
+	if (swkey->nsh.nsp) {
+		if (nsh_to_nlattr(skb, &output->nsh))
+			goto nla_put_failure;
+	}
+
 	if (swkey->phy.in_port == DP_MAX_PORTS) {
 		if (is_mask && (output->phy.in_port == 0xffff))
 			if (nla_put_u32(skb, OVS_KEY_ATTR_IN_PORT, 0xffffffff))