[ovs-dev] [PATCH] datapath: refactor netlink flow handling for mega flow support

Andy Zhou azhou at nicira.com
Thu May 2 19:36:35 UTC 2013


This patch refactors the netlink flow handling code to make
the processing, error checking and prerequisite checking
more stream lined.

Signed-off-by: Andy Zhou <azhou at nicira.com>
---
 datapath/datapath.c |   44 +++-
 datapath/flow.c     |  652 ++++++++++++++++++++++++++++-----------------------
 datapath/flow.h     |   16 +-
 3 files changed, 411 insertions(+), 301 deletions(-)

diff --git a/datapath/datapath.c b/datapath/datapath.c
index 42af315..8cf75bf 100644
--- a/datapath/datapath.c
+++ b/datapath/datapath.c
@@ -619,10 +619,14 @@ static int validate_tp_port(const struct sw_flow_key *flow_key)
 static int validate_and_copy_set_tun(const struct nlattr *attr,
 				     struct sw_flow_actions **sfa)
 {
-	struct ovs_key_ipv4_tunnel tun_key;
+	struct sw_flow_match match;
+	struct sw_flow_key key;
 	int err, start;
 
-	err = ipv4_tun_from_nlattr(nla_data(attr), &tun_key);
+	memset(&key, 0, sizeof key);
+	ovs_match_init(&match, &key);
+
+	err = ipv4_tun_from_nlattr(nla_data(attr), &match);
 	if (err)
 		return err;
 
@@ -630,7 +634,8 @@ static int validate_and_copy_set_tun(const struct nlattr *attr,
 	if (start < 0)
 		return start;
 
-	err = add_action(sfa, OVS_KEY_ATTR_IPV4_TUNNEL, &tun_key, sizeof(tun_key));
+	err = add_action(sfa, OVS_KEY_ATTR_IPV4_TUNNEL, &match.key->tun_key,
+                        sizeof(match.key->tun_key));
 	add_nested_action_end(*sfa, start);
 
 	return err;
@@ -1234,6 +1239,7 @@ static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, struct genl_info *info)
 	struct datapath *dp;
 	struct flow_table *table;
 	struct sw_flow_actions *acts = NULL;
+	struct sw_flow_match match;
 	int error;
 	int key_len;
 
@@ -1241,10 +1247,18 @@ static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, struct genl_info *info)
 	error = -EINVAL;
 	if (!a[OVS_FLOW_ATTR_KEY])
 		goto error;
-	error = ovs_flow_from_nlattrs(&key, &key_len, a[OVS_FLOW_ATTR_KEY]);
+
+	memset(&key, 0, sizeof key);
+	ovs_match_init(&match, &key);
+	error = ovs_match_from_nlattrs(&match, a[OVS_FLOW_ATTR_KEY]);
 	if (error)
 		goto error;
 
+	if (ovs_match_check_prerequisite(&match) == false)
+		goto error;
+
+	key_len = ovs_match_get_len(&match);
+
 	/* Validate actions. */
 	if (a[OVS_FLOW_ATTR_ACTIONS]) {
 		acts = ovs_flow_actions_alloc(nla_len(a[OVS_FLOW_ATTR_ACTIONS]));
@@ -1358,15 +1372,24 @@ static int ovs_flow_cmd_get(struct sk_buff *skb, struct genl_info *info)
 	struct sw_flow *flow;
 	struct datapath *dp;
 	struct flow_table *table;
+	struct sw_flow_match match;
 	int err;
 	int key_len;
 
+	memset(&key, 0, sizeof key);
+	ovs_match_init(&match, &key);
+
 	if (!a[OVS_FLOW_ATTR_KEY])
 		return -EINVAL;
-	err = ovs_flow_from_nlattrs(&key, &key_len, a[OVS_FLOW_ATTR_KEY]);
+	err = ovs_match_from_nlattrs(&match, a[OVS_FLOW_ATTR_KEY]);
 	if (err)
 		return err;
 
+	if (ovs_match_check_prerequisite(&match) == false)
+		return -EINVAL;
+
+	key_len = ovs_match_get_len(&match);
+
 	ovs_lock();
 	dp = get_dp(sock_net(skb->sk), ovs_header->dp_ifindex);
 	if (!dp) {
@@ -1404,6 +1427,7 @@ static int ovs_flow_cmd_del(struct sk_buff *skb, struct genl_info *info)
 	struct sw_flow *flow;
 	struct datapath *dp;
 	struct flow_table *table;
+	struct sw_flow_match match;
 	int err;
 	int key_len;
 
@@ -1418,10 +1442,18 @@ static int ovs_flow_cmd_del(struct sk_buff *skb, struct genl_info *info)
 		err = flush_flows(dp);
 		goto unlock;
 	}
-	err = ovs_flow_from_nlattrs(&key, &key_len, a[OVS_FLOW_ATTR_KEY]);
+
+	memset(&key, 0, sizeof key);
+	ovs_match_init(&match, &key);
+	err = ovs_match_from_nlattrs(&match, a[OVS_FLOW_ATTR_KEY]);
 	if (err)
 		goto unlock;
 
+	if (ovs_match_check_prerequisite(&match) == false)
+		goto unlock;
+
+	key_len = ovs_match_get_len(&match);
+
 	table = ovsl_dereference(dp->table);
 	flow = ovs_flow_tbl_lookup(table, &key, key_len);
 	if (!flow) {
diff --git a/datapath/flow.c b/datapath/flow.c
index 3ce926e..914051d 100644
--- a/datapath/flow.c
+++ b/datapath/flow.c
@@ -47,6 +47,110 @@
 
 static struct kmem_cache *flow_cache;
 
+static void field_add_writes__(struct sw_flow_match *match,
+		size_t offset, size_t size)
+{
+	int i;
+	u8 *op = &(match->n_writes[offset]);
+
+	for (i = 0; i < size; i++, op++) {
+		++*op;
+	}
+}
+
+#define SW_FLOW_KEY_PUT(match, field, value) \
+	do { \
+		field_add_writes__(match, offsetof(struct sw_flow_key, field), \
+				     sizeof (match)->key->field );             \
+		(match)->key->field = value;			               \
+	} while (0)
+
+#define SW_FLOW_KEY_MEMCPY(match, field, value_p, len) \
+	do { \
+		field_add_writes__(match, \
+                             offsetof(struct sw_flow_key, field), len); \
+		memcpy(&(match)->key->field, value_p, len); \
+	} while (0)
+
+void ovs_match_init(struct sw_flow_match *match, struct sw_flow_key *key)
+{
+	memset(match, 0, sizeof *match);
+	match->key = key;
+}
+
+bool ovs_match_check_prerequisite(const struct sw_flow_match *match)
+{
+	int i;
+	u64 expected = 0;
+
+	/* Any duplicate writes to the matching key is invalid. */
+	for (i=0; i< sizeof *(match->key); i++)
+		if (match->n_writes[i] >= 2) {
+			return false;
+        }
+
+	if (match->key->eth.type == htons(ETH_P_ARP))
+		expected |= 1ULL << OVS_KEY_ATTR_ARP;
+
+	if (match->key->eth.type == htons(ETH_P_RARP))
+		expected |= 1ULL << OVS_KEY_ATTR_ARP;
+
+	if (match->key->eth.type == htons(ETH_P_IP)) {
+		expected |= 1ULL << OVS_KEY_ATTR_IPV4;
+
+		if (match->key->ip.frag != OVS_FRAG_TYPE_LATER) {
+			if (match->key->ip.proto == IPPROTO_UDP)
+				expected |= 1ULL << OVS_KEY_ATTR_UDP;
+
+			if (match->key->ip.proto == IPPROTO_TCP)
+				expected |= 1ULL << OVS_KEY_ATTR_TCP;
+
+			if (match->key->ip.proto == IPPROTO_ICMP)
+				expected |= 1ULL << OVS_KEY_ATTR_ICMP;
+		}
+	}
+
+	if (match->key->eth.type == htons(ETH_P_IPV6)) {
+		expected |= 1ULL << OVS_KEY_ATTR_IPV6;
+
+		if (match->key->ip.frag != OVS_FRAG_TYPE_LATER) {
+			if (match->key->ip.proto == IPPROTO_UDP)
+				expected |= 1ULL << OVS_KEY_ATTR_UDP;
+
+			if (match->key->ip.proto == IPPROTO_TCP)
+				expected |= 1ULL << OVS_KEY_ATTR_TCP;
+
+			if (match->key->ip.proto == IPPROTO_ICMPV6) {
+				expected |= 1ULL << OVS_KEY_ATTR_ICMPV6;
+
+				if (match->key->ipv6.tp.src == htons(NDISC_NEIGHBOUR_SOLICITATION))
+					expected |= 1ULL << OVS_KEY_ATTR_ND;
+
+				if (match->key->ipv6.tp.src == htons(NDISC_NEIGHBOUR_ADVERTISEMENT))
+					expected |= 1ULL << OVS_KEY_ATTR_ND;
+			}
+		}
+	}
+
+	if ((match->key_attrs & expected) != expected)
+		return false;
+
+	return true;
+}
+
+int  ovs_match_get_len(struct sw_flow_match *match)
+{
+	int len;
+
+	for (len = sizeof *(match->key); len >0; len--) {
+		if (match->n_writes[len-1] > 0) {
+			return len;
+		}
+	}
+
+	return 0;
+}
+
 static int check_header(struct sk_buff *skb, int len)
 {
 	if (unlikely(skb->len < len))
@@ -850,112 +954,6 @@ const int ovs_key_lens[OVS_KEY_ATTR_MAX + 1] = {
 	[OVS_KEY_ATTR_TUNNEL] = -1,
 };
 
-static int ipv4_flow_from_nlattrs(struct sw_flow_key *swkey, int *key_len,
-				  const struct nlattr *a[], u64 *attrs)
-{
-	const struct ovs_key_icmp *icmp_key;
-	const struct ovs_key_tcp *tcp_key;
-	const struct ovs_key_udp *udp_key;
-
-	switch (swkey->ip.proto) {
-	case IPPROTO_TCP:
-		if (!(*attrs & (1 << OVS_KEY_ATTR_TCP)))
-			return -EINVAL;
-		*attrs &= ~(1 << OVS_KEY_ATTR_TCP);
-
-		*key_len = SW_FLOW_KEY_OFFSET(ipv4.tp);
-		tcp_key = nla_data(a[OVS_KEY_ATTR_TCP]);
-		swkey->ipv4.tp.src = tcp_key->tcp_src;
-		swkey->ipv4.tp.dst = tcp_key->tcp_dst;
-		break;
-
-	case IPPROTO_UDP:
-		if (!(*attrs & (1 << OVS_KEY_ATTR_UDP)))
-			return -EINVAL;
-		*attrs &= ~(1 << OVS_KEY_ATTR_UDP);
-
-		*key_len = SW_FLOW_KEY_OFFSET(ipv4.tp);
-		udp_key = nla_data(a[OVS_KEY_ATTR_UDP]);
-		swkey->ipv4.tp.src = udp_key->udp_src;
-		swkey->ipv4.tp.dst = udp_key->udp_dst;
-		break;
-
-	case IPPROTO_ICMP:
-		if (!(*attrs & (1 << OVS_KEY_ATTR_ICMP)))
-			return -EINVAL;
-		*attrs &= ~(1 << OVS_KEY_ATTR_ICMP);
-
-		*key_len = SW_FLOW_KEY_OFFSET(ipv4.tp);
-		icmp_key = nla_data(a[OVS_KEY_ATTR_ICMP]);
-		swkey->ipv4.tp.src = htons(icmp_key->icmp_type);
-		swkey->ipv4.tp.dst = htons(icmp_key->icmp_code);
-		break;
-	}
-
-	return 0;
-}
-
-static int ipv6_flow_from_nlattrs(struct sw_flow_key *swkey, int *key_len,
-				  const struct nlattr *a[], u64 *attrs)
-{
-	const struct ovs_key_icmpv6 *icmpv6_key;
-	const struct ovs_key_tcp *tcp_key;
-	const struct ovs_key_udp *udp_key;
-
-	switch (swkey->ip.proto) {
-	case IPPROTO_TCP:
-		if (!(*attrs & (1 << OVS_KEY_ATTR_TCP)))
-			return -EINVAL;
-		*attrs &= ~(1 << OVS_KEY_ATTR_TCP);
-
-		*key_len = SW_FLOW_KEY_OFFSET(ipv6.tp);
-		tcp_key = nla_data(a[OVS_KEY_ATTR_TCP]);
-		swkey->ipv6.tp.src = tcp_key->tcp_src;
-		swkey->ipv6.tp.dst = tcp_key->tcp_dst;
-		break;
-
-	case IPPROTO_UDP:
-		if (!(*attrs & (1 << OVS_KEY_ATTR_UDP)))
-			return -EINVAL;
-		*attrs &= ~(1 << OVS_KEY_ATTR_UDP);
-
-		*key_len = SW_FLOW_KEY_OFFSET(ipv6.tp);
-		udp_key = nla_data(a[OVS_KEY_ATTR_UDP]);
-		swkey->ipv6.tp.src = udp_key->udp_src;
-		swkey->ipv6.tp.dst = udp_key->udp_dst;
-		break;
-
-	case IPPROTO_ICMPV6:
-		if (!(*attrs & (1 << OVS_KEY_ATTR_ICMPV6)))
-			return -EINVAL;
-		*attrs &= ~(1 << OVS_KEY_ATTR_ICMPV6);
-
-		*key_len = SW_FLOW_KEY_OFFSET(ipv6.tp);
-		icmpv6_key = nla_data(a[OVS_KEY_ATTR_ICMPV6]);
-		swkey->ipv6.tp.src = htons(icmpv6_key->icmpv6_type);
-		swkey->ipv6.tp.dst = htons(icmpv6_key->icmpv6_code);
-
-		if (swkey->ipv6.tp.src == htons(NDISC_NEIGHBOUR_SOLICITATION) ||
-		    swkey->ipv6.tp.src == htons(NDISC_NEIGHBOUR_ADVERTISEMENT)) {
-			const struct ovs_key_nd *nd_key;
-
-			if (!(*attrs & (1 << OVS_KEY_ATTR_ND)))
-				return -EINVAL;
-			*attrs &= ~(1 << OVS_KEY_ATTR_ND);
-
-			*key_len = SW_FLOW_KEY_OFFSET(ipv6.nd);
-			nd_key = nla_data(a[OVS_KEY_ATTR_ND]);
-			memcpy(&swkey->ipv6.nd.target, nd_key->nd_target,
-			       sizeof(swkey->ipv6.nd.target));
-			memcpy(swkey->ipv6.nd.sll, nd_key->nd_sll, ETH_ALEN);
-			memcpy(swkey->ipv6.nd.tll, nd_key->nd_tll, ETH_ALEN);
-		}
-		break;
-	}
-
-	return 0;
-}
-
 static int parse_flow_nlattrs(const struct nlattr *attr,
 			      const struct nlattr *a[], u64 *attrsp)
 {
@@ -986,13 +984,12 @@ static int parse_flow_nlattrs(const struct nlattr *attr,
 }
 
 int ipv4_tun_from_nlattr(const struct nlattr *attr,
-			 struct ovs_key_ipv4_tunnel *tun_key)
+			 struct sw_flow_match *match)
 {
 	struct nlattr *a;
 	int rem;
 	bool ttl = false;
-
-	memset(tun_key, 0, sizeof(*tun_key));
+	u16 tun_flags = 0;
 
 	nla_for_each_nested(a, attr, rem) {
 		int type = nla_type(a);
@@ -1012,37 +1009,39 @@ int ipv4_tun_from_nlattr(const struct nlattr *attr,
 
 		switch (type) {
 		case OVS_TUNNEL_KEY_ATTR_ID:
-			tun_key->tun_id = nla_get_be64(a);
-			tun_key->tun_flags |= OVS_TNL_F_KEY;
+			SW_FLOW_KEY_PUT(match, tun_key.tun_id, nla_get_be64(a));
+			tun_flags |= OVS_TNL_F_KEY;
 			break;
 		case OVS_TUNNEL_KEY_ATTR_IPV4_SRC:
-			tun_key->ipv4_src = nla_get_be32(a);
+			SW_FLOW_KEY_PUT(match, tun_key.ipv4_src, nla_get_be32(a));
 			break;
 		case OVS_TUNNEL_KEY_ATTR_IPV4_DST:
-			tun_key->ipv4_dst = nla_get_be32(a);
+			SW_FLOW_KEY_PUT(match, tun_key.ipv4_dst, nla_get_be32(a));
 			break;
 		case OVS_TUNNEL_KEY_ATTR_TOS:
-			tun_key->ipv4_tos = nla_get_u8(a);
+			SW_FLOW_KEY_PUT(match, tun_key.ipv4_tos, nla_get_u8(a));
 			break;
 		case OVS_TUNNEL_KEY_ATTR_TTL:
-			tun_key->ipv4_ttl = nla_get_u8(a);
+			SW_FLOW_KEY_PUT(match, tun_key.ipv4_ttl, nla_get_u8(a));
 			ttl = true;
 			break;
 		case OVS_TUNNEL_KEY_ATTR_DONT_FRAGMENT:
-			tun_key->tun_flags |= OVS_TNL_F_DONT_FRAGMENT;
+			tun_flags |= OVS_TNL_F_DONT_FRAGMENT;
 			break;
 		case OVS_TUNNEL_KEY_ATTR_CSUM:
-			tun_key->tun_flags |= OVS_TNL_F_CSUM;
+			tun_flags |= OVS_TNL_F_CSUM;
 			break;
 		default:
 			return -EINVAL;
 
 		}
 	}
+	SW_FLOW_KEY_PUT(match, tun_key.tun_flags,  tun_flags);
+
 	if (rem > 0)
 		return -EINVAL;
 
-	if (!tun_key->ipv4_dst)
+	if (!match->key->tun_key.ipv4_dst)
 		return -EINVAL;
 
 	if (!ttl)
@@ -1086,180 +1085,267 @@ int ipv4_tun_to_nlattr(struct sk_buff *skb,
 
 /**
  * ovs_flow_from_nlattrs - parses Netlink attributes into a flow key.
- * @swkey: receives the extracted flow key.
- * @key_lenp: number of bytes used in @swkey.
+ * @match: receives the extracted flow match information.
  * @attr: Netlink attribute holding nested %OVS_KEY_ATTR_* Netlink attribute
  * sequence.
  */
-int ovs_flow_from_nlattrs(struct sw_flow_key *swkey, int *key_lenp,
-		      const struct nlattr *attr)
+int ovs_match_from_nlattrs(struct sw_flow_match *match,
+		const struct nlattr *attr)
 {
 	const struct nlattr *a[OVS_KEY_ATTR_MAX + 1];
-	const struct ovs_key_ethernet *eth_key;
-	int key_len;
+	const struct nlattr *encap;
 	u64 attrs;
 	int err;
-
-	memset(swkey, 0, sizeof(struct sw_flow_key));
-	key_len = SW_FLOW_KEY_OFFSET(eth);
+	int k;
 
 	err = parse_flow_nlattrs(attr, a, &attrs);
 	if (err)
 		return err;
 
-	/* Metadata attributes. */
-	if (attrs & (1 << OVS_KEY_ATTR_PRIORITY)) {
-		swkey->phy.priority = nla_get_u32(a[OVS_KEY_ATTR_PRIORITY]);
-		attrs &= ~(1 << OVS_KEY_ATTR_PRIORITY);
-	}
-	if (attrs & (1 << OVS_KEY_ATTR_IN_PORT)) {
-		u32 in_port = nla_get_u32(a[OVS_KEY_ATTR_IN_PORT]);
-		if (in_port >= DP_MAX_PORTS)
-			return -EINVAL;
-		swkey->phy.in_port = in_port;
-		attrs &= ~(1 << OVS_KEY_ATTR_IN_PORT);
-	} else {
-		swkey->phy.in_port = DP_MAX_PORTS;
+	if (attrs & 1ULL << OVS_KEY_ATTR_ENCAP) {
+		encap = a[OVS_KEY_ATTR_ENCAP];
+		attrs &= ~(1ULL << OVS_KEY_ATTR_ENCAP);
+		if (nla_len(encap)) {
+			__be16 tci = 0;
+			__be16 eth_type = 0; /* ETH_P_8021Q */
+
+			if (a[OVS_KEY_ATTR_ETHERTYPE])
+				eth_type = nla_get_be16(a[OVS_KEY_ATTR_ETHERTYPE]);
+			if (a[OVS_KEY_ATTR_VLAN])
+				tci = nla_get_be16(a[OVS_KEY_ATTR_VLAN]);
+
+			if  ( (eth_type == htons(ETH_P_8021Q))
+					&& (tci & htons(VLAN_TAG_PRESENT)) )
+				err = parse_flow_nlattrs(encap, a, &attrs);
+			else
+				err = -EINVAL;
+
+			if (err)
+				return err;
+		}
 	}
-	if (attrs & (1 << OVS_KEY_ATTR_SKB_MARK)) {
-		uint32_t mark = nla_get_u32(a[OVS_KEY_ATTR_SKB_MARK]);
+
+	/* Save all attributes specified for prerequisite check */
+	match->key_attrs = attrs;
+
+	/* Set default values if not specified. */
+	if ((attrs & (1 << OVS_KEY_ATTR_IN_PORT)) == 0)
+		SW_FLOW_KEY_PUT(match, phy.in_port, DP_MAX_PORTS);
+
+	if ((attrs & (1 << OVS_KEY_ATTR_ETHERTYPE)) == 0)
+		SW_FLOW_KEY_PUT(match, eth.type, htons(ETH_P_802_2));
+
+	while (attrs) {
+		k = fls64(attrs) -1;
+		attrs &= ~(1ULL << k);
+
+		switch(k) {
+		case OVS_KEY_ATTR_PRIORITY:
+			SW_FLOW_KEY_PUT(match, phy.priority,
+				  nla_get_u32(a[OVS_KEY_ATTR_PRIORITY]));
+			break;
+
+		case OVS_KEY_ATTR_IN_PORT:
+			{
+				u32 in_port = nla_get_u32(a[OVS_KEY_ATTR_IN_PORT]);
+
+				if (in_port >= DP_MAX_PORTS)
+					return -EINVAL;
+				SW_FLOW_KEY_PUT(match, phy.in_port, in_port);
+			}
+			break;
+
+		case OVS_KEY_ATTR_SKB_MARK:
+			{
+				uint32_t mark = nla_get_u32(a[OVS_KEY_ATTR_SKB_MARK]);
+
 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) && !defined(CONFIG_NETFILTER)
-		if (mark != 0)
-			return -EINVAL;
+				if (mark != 0)
+					return -EINVAL;
 #endif
-		swkey->phy.skb_mark = mark;
-		attrs &= ~(1 << OVS_KEY_ATTR_SKB_MARK);
-	}
+				SW_FLOW_KEY_PUT(match, phy.skb_mark, mark);
+			}
+			break;
 
-	if (attrs & (1ULL << OVS_KEY_ATTR_TUNNEL)) {
-		err = ipv4_tun_from_nlattr(a[OVS_KEY_ATTR_TUNNEL], &swkey->tun_key);
-		if (err)
-			return err;
+		case OVS_KEY_ATTR_TUNNEL:
+			if (ipv4_tun_from_nlattr(a[OVS_KEY_ATTR_TUNNEL], match))
+				return -EINVAL;
 
-		attrs &= ~(1ULL << OVS_KEY_ATTR_TUNNEL);
-	}
+			break;
 
-	/* Data attributes. */
-	if (!(attrs & (1 << OVS_KEY_ATTR_ETHERNET)))
-		return -EINVAL;
-	attrs &= ~(1 << OVS_KEY_ATTR_ETHERNET);
+		case OVS_KEY_ATTR_ETHERNET:
+			{
+				const struct ovs_key_ethernet *eth_key;
 
-	eth_key = nla_data(a[OVS_KEY_ATTR_ETHERNET]);
-	memcpy(swkey->eth.src, eth_key->eth_src, ETH_ALEN);
-	memcpy(swkey->eth.dst, eth_key->eth_dst, ETH_ALEN);
+				eth_key = nla_data(a[OVS_KEY_ATTR_ETHERNET]);
+				SW_FLOW_KEY_MEMCPY(match, eth.src,
+						eth_key->eth_src, ETH_ALEN);
+				SW_FLOW_KEY_MEMCPY(match, eth.dst,
+						eth_key->eth_dst, ETH_ALEN);
+			}
+			break;
 
-	if (attrs & (1u << OVS_KEY_ATTR_ETHERTYPE) &&
-	    nla_get_be16(a[OVS_KEY_ATTR_ETHERTYPE]) == htons(ETH_P_8021Q)) {
-		const struct nlattr *encap;
-		__be16 tci;
+		case OVS_KEY_ATTR_VLAN:
+			{
+				__be16 tci;
 
-		if (attrs != ((1 << OVS_KEY_ATTR_VLAN) |
-			      (1 << OVS_KEY_ATTR_ETHERTYPE) |
-			      (1 << OVS_KEY_ATTR_ENCAP)))
-			return -EINVAL;
+				tci = nla_get_be16(a[OVS_KEY_ATTR_VLAN]);
+				SW_FLOW_KEY_PUT(match, eth.tci, tci);
+			}
+			break;
 
-		encap = a[OVS_KEY_ATTR_ENCAP];
-		tci = nla_get_be16(a[OVS_KEY_ATTR_VLAN]);
-		if (tci & htons(VLAN_TAG_PRESENT)) {
-			swkey->eth.tci = tci;
+		case OVS_KEY_ATTR_ETHERTYPE:
+			{
+				__be16 eth_type;
 
-			err = parse_flow_nlattrs(encap, a, &attrs);
-			if (err)
-				return err;
-		} else if (!tci) {
-			/* Corner case for truncated 802.1Q header. */
-			if (nla_len(encap))
-				return -EINVAL;
+				eth_type = nla_get_be16(a[OVS_KEY_ATTR_ETHERTYPE]);
 
-			swkey->eth.type = htons(ETH_P_8021Q);
-			*key_lenp = key_len;
-			return 0;
-		} else {
-			return -EINVAL;
-		}
-	}
+				if (ntohs(eth_type) < ETH_P_802_3_MIN)
+					return -EINVAL;
 
-	if (attrs & (1 << OVS_KEY_ATTR_ETHERTYPE)) {
-		swkey->eth.type = nla_get_be16(a[OVS_KEY_ATTR_ETHERTYPE]);
-		if (ntohs(swkey->eth.type) < ETH_P_802_3_MIN)
-			return -EINVAL;
-		attrs &= ~(1 << OVS_KEY_ATTR_ETHERTYPE);
-	} else {
-		swkey->eth.type = htons(ETH_P_802_2);
-	}
+				SW_FLOW_KEY_PUT(match, eth.type, eth_type);
+			}
+			break;
 
-	if (swkey->eth.type == htons(ETH_P_IP)) {
-		const struct ovs_key_ipv4 *ipv4_key;
+		case OVS_KEY_ATTR_IPV4:
+			{
+				const struct ovs_key_ipv4 *ipv4_key;
 
-		if (!(attrs & (1 << OVS_KEY_ATTR_IPV4)))
-			return -EINVAL;
-		attrs &= ~(1 << OVS_KEY_ATTR_IPV4);
+				ipv4_key = nla_data(a[OVS_KEY_ATTR_IPV4]);
+				if (ipv4_key->ipv4_frag > OVS_FRAG_TYPE_MAX)
+					return -EINVAL;
+				SW_FLOW_KEY_PUT(match, ip.proto,
+						ipv4_key->ipv4_proto);
+				SW_FLOW_KEY_PUT(match, ip.tos,
+						ipv4_key->ipv4_tos);
+				SW_FLOW_KEY_PUT(match, ip.ttl,
+						ipv4_key->ipv4_ttl);
+				SW_FLOW_KEY_PUT(match, ip.frag,
+						ipv4_key->ipv4_frag);
+				SW_FLOW_KEY_PUT(match, ipv4.addr.src,
+						ipv4_key->ipv4_src);
+				SW_FLOW_KEY_PUT(match, ipv4.addr.dst,
+						ipv4_key->ipv4_dst);
+			}
+			break;
 
-		key_len = SW_FLOW_KEY_OFFSET(ipv4.addr);
-		ipv4_key = nla_data(a[OVS_KEY_ATTR_IPV4]);
-		if (ipv4_key->ipv4_frag > OVS_FRAG_TYPE_MAX)
-			return -EINVAL;
-		swkey->ip.proto = ipv4_key->ipv4_proto;
-		swkey->ip.tos = ipv4_key->ipv4_tos;
-		swkey->ip.ttl = ipv4_key->ipv4_ttl;
-		swkey->ip.frag = ipv4_key->ipv4_frag;
-		swkey->ipv4.addr.src = ipv4_key->ipv4_src;
-		swkey->ipv4.addr.dst = ipv4_key->ipv4_dst;
-
-		if (swkey->ip.frag != OVS_FRAG_TYPE_LATER) {
-			err = ipv4_flow_from_nlattrs(swkey, &key_len, a, &attrs);
-			if (err)
-				return err;
-		}
-	} else if (swkey->eth.type == htons(ETH_P_IPV6)) {
-		const struct ovs_key_ipv6 *ipv6_key;
+		case OVS_KEY_ATTR_IPV6:
+			{
+				const struct ovs_key_ipv6 *ipv6_key;
 
-		if (!(attrs & (1 << OVS_KEY_ATTR_IPV6)))
-			return -EINVAL;
-		attrs &= ~(1 << OVS_KEY_ATTR_IPV6);
+				ipv6_key = nla_data(a[OVS_KEY_ATTR_IPV6]);
+				if (ipv6_key->ipv6_frag > OVS_FRAG_TYPE_MAX)
+					return -EINVAL;
+				SW_FLOW_KEY_PUT(match, ipv6.label,
+						ipv6_key->ipv6_label);
+				SW_FLOW_KEY_PUT(match, ip.proto,
+						ipv6_key->ipv6_proto);
+				SW_FLOW_KEY_PUT(match, ip.tos,
+						ipv6_key->ipv6_tclass);
+				SW_FLOW_KEY_PUT(match, ip.ttl,
+						ipv6_key->ipv6_hlimit);
+				SW_FLOW_KEY_PUT(match, ip.frag,
+						ipv6_key->ipv6_frag);
+				SW_FLOW_KEY_MEMCPY(match, ipv6.addr.src,
+						ipv6_key->ipv6_src,
+						sizeof(match->key->ipv6.addr.src));
+				SW_FLOW_KEY_MEMCPY(match, ipv6.addr.dst,
+						ipv6_key->ipv6_dst,
+						sizeof(match->key->ipv6.addr.dst));
 
-		key_len = SW_FLOW_KEY_OFFSET(ipv6.label);
-		ipv6_key = nla_data(a[OVS_KEY_ATTR_IPV6]);
-		if (ipv6_key->ipv6_frag > OVS_FRAG_TYPE_MAX)
-			return -EINVAL;
-		swkey->ipv6.label = ipv6_key->ipv6_label;
-		swkey->ip.proto = ipv6_key->ipv6_proto;
-		swkey->ip.tos = ipv6_key->ipv6_tclass;
-		swkey->ip.ttl = ipv6_key->ipv6_hlimit;
-		swkey->ip.frag = ipv6_key->ipv6_frag;
-		memcpy(&swkey->ipv6.addr.src, ipv6_key->ipv6_src,
-		       sizeof(swkey->ipv6.addr.src));
-		memcpy(&swkey->ipv6.addr.dst, ipv6_key->ipv6_dst,
-		       sizeof(swkey->ipv6.addr.dst));
-
-		if (swkey->ip.frag != OVS_FRAG_TYPE_LATER) {
-			err = ipv6_flow_from_nlattrs(swkey, &key_len, a, &attrs);
-			if (err)
-				return err;
-		}
-	} else if (swkey->eth.type == htons(ETH_P_ARP) ||
-		   swkey->eth.type == htons(ETH_P_RARP)) {
-		const struct ovs_key_arp *arp_key;
+			}
+			break;
 
-		if (!(attrs & (1 << OVS_KEY_ATTR_ARP)))
-			return -EINVAL;
-		attrs &= ~(1 << OVS_KEY_ATTR_ARP);
+		case OVS_KEY_ATTR_ARP:
+			{
+				const struct ovs_key_arp *arp_key;
 
-		key_len = SW_FLOW_KEY_OFFSET(ipv4.arp);
-		arp_key = nla_data(a[OVS_KEY_ATTR_ARP]);
-		swkey->ipv4.addr.src = arp_key->arp_sip;
-		swkey->ipv4.addr.dst = arp_key->arp_tip;
-		if (arp_key->arp_op & htons(0xff00))
+				arp_key = nla_data(a[OVS_KEY_ATTR_ARP]);
+				SW_FLOW_KEY_PUT(match, ipv4.addr.src,
+						arp_key->arp_sip);
+				SW_FLOW_KEY_PUT(match, ipv4.addr.dst,
+						arp_key->arp_tip);
+				if (arp_key->arp_op & htons(0xff00))
+					return -EINVAL;
+				SW_FLOW_KEY_PUT(match, ip.proto,
+						ntohs(arp_key->arp_op));
+				SW_FLOW_KEY_MEMCPY(match, ipv4.arp.sha,
+						arp_key->arp_sha,
+						ETH_ALEN);
+				SW_FLOW_KEY_MEMCPY(match, ipv4.arp.tha,
+						arp_key->arp_tha,
+						ETH_ALEN);
+			}
+			break;
+
+		case OVS_KEY_ATTR_TCP:
+			{
+				const struct ovs_key_tcp *tcp_key;
+
+				tcp_key = nla_data(a[OVS_KEY_ATTR_TCP]);
+				SW_FLOW_KEY_PUT(match, ipv4.tp.src,
+						tcp_key->tcp_src);
+				SW_FLOW_KEY_PUT(match, ipv4.tp.dst,
+						tcp_key->tcp_dst);
+			}
+			break;
+
+		case OVS_KEY_ATTR_UDP:
+			{
+				const struct ovs_key_udp *udp_key;
+
+				udp_key = nla_data(a[OVS_KEY_ATTR_UDP]);
+				SW_FLOW_KEY_PUT(match, ipv4.tp.src,
+						udp_key->udp_src);
+				SW_FLOW_KEY_PUT(match, ipv4.tp.dst,
+						udp_key->udp_dst);
+			}
+			break;
+
+		case OVS_KEY_ATTR_ICMP:
+			{
+				const struct ovs_key_icmp *icmp_key;
+
+				icmp_key = nla_data(a[OVS_KEY_ATTR_ICMP]);
+				SW_FLOW_KEY_PUT(match, ipv4.tp.src,
+						htons(icmp_key->icmp_type));
+				SW_FLOW_KEY_PUT(match, ipv4.tp.dst,
+						htons(icmp_key->icmp_code));
+			}
+			break;
+
+		case OVS_KEY_ATTR_ICMPV6:
+			{
+				const struct ovs_key_icmpv6 *icmpv6_key;
+
+				icmpv6_key = nla_data(a[OVS_KEY_ATTR_ICMPV6]);
+				SW_FLOW_KEY_PUT(match, ipv6.tp.src,
+						htons(icmpv6_key->icmpv6_type));
+				SW_FLOW_KEY_PUT(match, ipv6.tp.dst,
+						htons(icmpv6_key->icmpv6_code));
+			}
+			break;
+
+		case OVS_KEY_ATTR_ND:
+			{
+				const struct ovs_key_nd *nd_key;
+
+				nd_key = nla_data(a[OVS_KEY_ATTR_ND]);
+				SW_FLOW_KEY_MEMCPY(match, ipv6.nd.target,
+					nd_key->nd_target,
+					sizeof(match->key->ipv6.nd.target));
+				SW_FLOW_KEY_MEMCPY(match, ipv6.nd.sll,
+						nd_key->nd_sll, ETH_ALEN);
+				SW_FLOW_KEY_MEMCPY(match, ipv6.nd.tll,
+						nd_key->nd_tll, ETH_ALEN);
+			}
+			break;
+
+		default:
 			return -EINVAL;
-		swkey->ip.proto = ntohs(arp_key->arp_op);
-		memcpy(swkey->ipv4.arp.sha, arp_key->arp_sha, ETH_ALEN);
-		memcpy(swkey->ipv4.arp.tha, arp_key->arp_tha, ETH_ALEN);
+		}
 	}
 
-	if (attrs)
-		return -EINVAL;
-	*key_lenp = key_len;
-
 	return 0;
 }
 
@@ -1279,51 +1365,31 @@ int ovs_flow_from_nlattrs(struct sw_flow_key *swkey, int *key_lenp,
 int ovs_flow_metadata_from_nlattrs(struct sw_flow *flow, int key_len, const struct nlattr *attr)
 {
 	struct ovs_key_ipv4_tunnel *tun_key = &flow->key.tun_key;
-	const struct nlattr *nla;
-	int rem;
+	const struct nlattr *a[OVS_KEY_ATTR_MAX + 1];
+	u64 attrs;
+	int err;
+	struct sw_flow_match match;
 
 	flow->key.phy.in_port = DP_MAX_PORTS;
 	flow->key.phy.priority = 0;
 	flow->key.phy.skb_mark = 0;
 	memset(tun_key, 0, sizeof(flow->key.tun_key));
 
-	nla_for_each_nested(nla, attr, rem) {
-		int type = nla_type(nla);
-
-		if (type <= OVS_KEY_ATTR_MAX && ovs_key_lens[type] > 0) {
-			int err;
-
-			if (nla_len(nla) != ovs_key_lens[type])
-				return -EINVAL;
-
-			switch (type) {
-			case OVS_KEY_ATTR_PRIORITY:
-				flow->key.phy.priority = nla_get_u32(nla);
-				break;
+	err = parse_flow_nlattrs(attr, a, &attrs);
+	if (err)
+		return -EINVAL;
 
-			case OVS_KEY_ATTR_TUNNEL:
-				err = ipv4_tun_from_nlattr(nla, tun_key);
-				if (err)
-					return err;
-				break;
+	attrs &= (1 << OVS_KEY_ATTR_PRIORITY);
+	attrs &= (1 << OVS_KEY_ATTR_TUNNEL);
+	attrs &= (1 << OVS_KEY_ATTR_IN_PORT);
+	attrs &= (1 << OVS_KEY_ATTR_SKB_MARK);
 
-			case OVS_KEY_ATTR_IN_PORT:
-				if (nla_get_u32(nla) >= DP_MAX_PORTS)
-					return -EINVAL;
-				flow->key.phy.in_port = nla_get_u32(nla);
-				break;
+	if (attrs)
+		return -EINVAL;
 
-			case OVS_KEY_ATTR_SKB_MARK:
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) && !defined(CONFIG_NETFILTER)
-				if (nla_get_u32(nla) != 0)
-					return -EINVAL;
-#endif
-				flow->key.phy.skb_mark = nla_get_u32(nla);
-				break;
-			}
-		}
-	}
-	if (rem)
+	ovs_match_init(&match, &flow->key);
+	err = ovs_match_from_nlattrs(&match, attr);
+	if (err)
 		return -EINVAL;
 
 	flow->hash = ovs_flow_hash(&flow->key,
diff --git a/datapath/flow.h b/datapath/flow.h
index dba66cf..09b1482 100644
--- a/datapath/flow.h
+++ b/datapath/flow.h
@@ -129,6 +129,16 @@ struct sw_flow {
 	u8 tcp_flags;		/* Union of seen TCP flags. */
 };
 
+struct sw_flow_match {
+	struct sw_flow_key *key;
+	u8 n_writes[sizeof(struct sw_flow_key)];  /* Per byte write count. */
+	u64  key_attrs;   /* OVS_KEY_ATTR_XXXs used to populate the keys */
+};
+
+void ovs_match_init(struct sw_flow_match *match, struct sw_flow_key *key);
+bool ovs_match_check_prerequisite(const struct sw_flow_match *match);
+int  ovs_match_get_len(struct sw_flow_match *match);
+
 struct arp_eth_header {
 	__be16      ar_hrd;	/* format of hardware address   */
 	__be16      ar_pro;	/* format of protocol address   */
@@ -159,7 +169,7 @@ void ovs_flow_used(struct sw_flow *, struct sk_buff *);
 u64 ovs_flow_used_time(unsigned long flow_jiffies);
 
 int ovs_flow_to_nlattrs(const struct sw_flow_key *, struct sk_buff *);
-int ovs_flow_from_nlattrs(struct sw_flow_key *swkey, int *key_lenp,
+int ovs_match_from_nlattrs(struct sw_flow_match *match,
 		      const struct nlattr *);
 int ovs_flow_metadata_from_nlattrs(struct sw_flow *flow, int key_len,
 				   const struct nlattr *attr);
@@ -176,6 +186,8 @@ struct flow_table {
 	bool keep_flows;
 };
 
+
+
 static inline int ovs_flow_tbl_count(struct flow_table *table)
 {
 	return table->count;
@@ -200,7 +212,7 @@ void ovs_flow_tbl_remove(struct flow_table *table, struct sw_flow *flow);
 struct sw_flow *ovs_flow_tbl_next(struct flow_table *table, u32 *bucket, u32 *idx);
 extern const int ovs_key_lens[OVS_KEY_ATTR_MAX + 1];
 int ipv4_tun_from_nlattr(const struct nlattr *attr,
-			 struct ovs_key_ipv4_tunnel *tun_key);
+			 struct sw_flow_match *match);
 int ipv4_tun_to_nlattr(struct sk_buff *skb,
 			const struct ovs_key_ipv4_tunnel *tun_key);
 
-- 
1.7.9.5




More information about the dev mailing list