[ovs-dev] [PATCH] v2 datapath: More flexible kernel/userspace tunneling attribute.

Pravin B Shelar pshelar at nicira.com
Wed Jan 16 21:54:39 UTC 2013


Fixed according to comments from Jesse,
v1-v2:
 - Fixed sample action.
 - Fixed set ipv4-tunnel.
 - Converted tunnel flags to netlink flags.
 - Fixed dump-flow.

--8<--------------------------cut here-------------------------->8--

Following patch breaks down single ipv4_tunnel netlink attribute into
individual member attributes. It will help when we extend tunneling
parameters in future.

Signed-off-by: Pravin B Shelar <pshelar at nicira.com>
Bug #14611
---
 datapath/datapath.c         |  327 +++++++++++++++++++++++++++++++++++--------
 datapath/flow.c             |  172 +++++++++++++++++------
 datapath/flow.h             |   16 ++-
 datapath/tunnel.h           |    5 +
 include/linux/openvswitch.h |   31 ++--
 lib/dpif-netdev.c           |    2 +-
 lib/odp-util.c              |  236 ++++++++++++++++++-------------
 tests/odp.at                |   11 +-
 8 files changed, 582 insertions(+), 218 deletions(-)

diff --git a/datapath/datapath.c b/datapath/datapath.c
index ed69af8..4ed40e2 100644
--- a/datapath/datapath.c
+++ b/datapath/datapath.c
@@ -423,16 +423,83 @@ static int flush_flows(struct datapath *dp)
 	return 0;
 }
 
-static int validate_actions(const struct nlattr *attr,
-				const struct sw_flow_key *key, int depth);
+static struct nlattr *reserve_sfa_size(struct sw_flow_actions **sfa, int attr_len)
+{
+
+	struct sw_flow_actions *new;
+	struct nlattr *a;
+
+	if (NLA_ALIGN(attr_len) <= (ksize(*sfa) - (*sfa)->actions_len))
+		goto out;
+
+	if (ksize(*sfa) * 2 > MAX_ACTIONS_BUFSIZE)
+		return ERR_PTR(-EMSGSIZE);
+
+	new = ovs_flow_actions_alloc(ksize(*sfa) * 2);
+	if (IS_ERR(new))
+		return (void *)new;
+
+	memcpy(new->actions, (*sfa)->actions, (*sfa)->actions_len);
+	new->actions_len = (*sfa)->actions_len;
+	kfree(*sfa);
+	*sfa = new;
+
+out:
+	a = (struct nlattr *) ((unsigned char *)(*sfa)->actions +
+						(*sfa)->actions_len);
+	(*sfa)->actions_len = (*sfa)->actions_len + NLA_ALIGN(attr_len);
+	return  a;
+}
 
-static int validate_sample(const struct nlattr *attr,
-				const struct sw_flow_key *key, int depth)
+static int add_action(struct sw_flow_actions **sfa, int attrtype, void *data, int len)
+{
+	struct nlattr *a;
+
+	a = reserve_sfa_size(sfa, nla_attr_size(len));
+	if (IS_ERR(a))
+		return PTR_ERR(a);
+
+	a->nla_type = attrtype;
+	a->nla_len = nla_attr_size(len);
+
+	if (data)
+		memcpy(nla_data(a), data, len);
+	memset((unsigned char *) a + a->nla_len, 0, nla_padlen(len));
+
+	return 0;
+}
+
+static inline int add_nested_action_start(struct sw_flow_actions **sfa, int attrtype)
+{
+	int used = (*sfa)->actions_len;
+	int err;
+
+	err = add_action(sfa, attrtype, NULL, 0);
+	if (err)
+		return err;
+
+	return used;
+}
+
+static inline void add_nested_action_end(struct sw_flow_actions *sfa, int st_offset)
+{
+	struct nlattr *a = (struct nlattr *) ((unsigned char *)sfa->actions + st_offset);
+
+	a->nla_len = sfa->actions_len - st_offset;
+}
+
+static int validate_and_copy_actions(const struct nlattr *attr,
+				const struct sw_flow_key *key, int depth,
+				struct sw_flow_actions **sfa);
+
+static int validate_and_copy_sample(const struct nlattr *attr,
+			   const struct sw_flow_key *key, int depth,
+			   struct sw_flow_actions **sfa)
 {
 	const struct nlattr *attrs[OVS_SAMPLE_ATTR_MAX + 1];
 	const struct nlattr *probability, *actions;
 	const struct nlattr *a;
-	int rem;
+	int rem, start, err, st_acts;
 
 	memset(attrs, 0, sizeof(attrs));
 	nla_for_each_nested(a, attr, rem) {
@@ -451,7 +518,26 @@ static int validate_sample(const struct nlattr *attr,
 	actions = attrs[OVS_SAMPLE_ATTR_ACTIONS];
 	if (!actions || (nla_len(actions) && nla_len(actions) < NLA_HDRLEN))
 		return -EINVAL;
-	return validate_actions(actions, key, depth + 1);
+
+	/* validation done, copy sample action. */
+	start = add_nested_action_start(sfa, OVS_ACTION_ATTR_SAMPLE);
+	if (start < 0)
+		return start;
+	err = add_action(sfa, OVS_SAMPLE_ATTR_PROBABILITY, nla_data(probability), sizeof(u32));
+	if (err)
+		return err;
+	st_acts = add_nested_action_start(sfa, OVS_SAMPLE_ATTR_ACTIONS);
+	if (st_acts < 0)
+		return st_acts;
+
+	err = validate_and_copy_actions(actions, key, depth + 1, sfa);
+	if (err)
+		return err;
+
+	add_nested_action_end(*sfa, st_acts);
+	add_nested_action_end(*sfa, start);
+
+	return 0;
 }
 
 static int validate_tp_port(const struct sw_flow_key *flow_key)
@@ -467,8 +553,30 @@ static int validate_tp_port(const struct sw_flow_key *flow_key)
 	return -EINVAL;
 }
 
+static int validate_and_copy_set_tun(const struct nlattr *attr,
+				     struct sw_flow_actions **sfa)
+{
+	struct ovs_key_ipv4_tunnel tun_key;
+	int err, start;
+
+	err = ipv4_tun_from_nlattr(attr, &tun_key);
+	if (err)
+		return err;
+
+	start = add_nested_action_start(sfa, OVS_ACTION_ATTR_SET);
+	if (start < 0)
+		return start;
+
+	err = add_action(sfa, OVS_KEY_ATTR_IPV4_TUNNEL, &tun_key, sizeof(tun_key));
+	add_nested_action_end(*sfa, start);
+
+	return err;
+}
+
 static int validate_set(const struct nlattr *a,
-			const struct sw_flow_key *flow_key)
+			const struct sw_flow_key *flow_key,
+			struct sw_flow_actions **sfa,
+			bool *set_tun)
 {
 	const struct nlattr *ovs_key = nla_data(a);
 	int key_type = nla_type(ovs_key);
@@ -478,13 +586,14 @@ static int validate_set(const struct nlattr *a,
 		return -EINVAL;
 
 	if (key_type > OVS_KEY_ATTR_MAX ||
-	    nla_len(ovs_key) != ovs_key_lens[key_type])
+	    (ovs_key_lens[key_type] != nla_len(ovs_key) &&
+	     ovs_key_lens[key_type] != -1))
 		return -EINVAL;
 
 	switch (key_type) {
 	const struct ovs_key_ipv4 *ipv4_key;
-	const struct ovs_key_ipv4_tunnel *tun_key;
 	const struct ovs_key_ipv6 *ipv6_key;
+	int err;
 
 	case OVS_KEY_ATTR_PRIORITY:
 	case OVS_KEY_ATTR_TUN_ID:
@@ -498,10 +607,11 @@ static int validate_set(const struct nlattr *a,
 #endif
 		break;
 
-	case OVS_KEY_ATTR_IPV4_TUNNEL:
-		tun_key = nla_data(ovs_key);
-		if (!tun_key->ipv4_dst)
-			return -EINVAL;
+	case OVS_KEY_ATTR_TUNNEL:
+		*set_tun = true;
+		err = validate_and_copy_set_tun(a, sfa);
+		if (err)
+			return err;
 		break;
 
 	case OVS_KEY_ATTR_IPV4:
@@ -579,8 +689,24 @@ static int validate_userspace(const struct nlattr *attr)
 	return 0;
 }
 
-static int validate_actions(const struct nlattr *attr,
-				const struct sw_flow_key *key,  int depth)
+static int copy_action(const struct nlattr *from,
+		      struct sw_flow_actions **sfa)
+{
+	int totlen = NLA_ALIGN(from->nla_len);
+	struct nlattr *to;
+
+	to = reserve_sfa_size(sfa, from->nla_len);
+	if (IS_ERR(to))
+		return PTR_ERR(to);
+
+	memcpy(to, from, totlen);
+	return 0;
+}
+
+static int validate_and_copy_actions(const struct nlattr *attr,
+				const struct sw_flow_key *key,
+				int depth,
+				struct sw_flow_actions **sfa)
 {
 	const struct nlattr *a;
 	int rem, err;
@@ -600,12 +726,14 @@ static int validate_actions(const struct nlattr *attr,
 		};
 		const struct ovs_action_push_vlan *vlan;
 		int type = nla_type(a);
+		bool skip_copy;
 
 		if (type > OVS_ACTION_ATTR_MAX ||
 		    (action_lens[type] != nla_len(a) &&
 		     action_lens[type] != (u32)-1))
 			return -EINVAL;
 
+		skip_copy = false;
 		switch (type) {
 		case OVS_ACTION_ATTR_UNSPEC:
 			return -EINVAL;
@@ -634,20 +762,26 @@ static int validate_actions(const struct nlattr *attr,
 			break;
 
 		case OVS_ACTION_ATTR_SET:
-			err = validate_set(a, key);
+			err = validate_set(a, key, sfa, &skip_copy);
 			if (err)
 				return err;
 			break;
 
 		case OVS_ACTION_ATTR_SAMPLE:
-			err = validate_sample(a, key, depth);
+			err = validate_and_copy_sample(a, key, depth, sfa);
 			if (err)
 				return err;
+			skip_copy = true;
 			break;
 
 		default:
 			return -EINVAL;
 		}
+		if (!skip_copy) {
+			err = copy_action(a, sfa);
+			if (err)
+				return err;
+		}
 	}
 
 	if (rem > 0)
@@ -716,16 +850,15 @@ static int ovs_packet_cmd_execute(struct sk_buff *skb, struct genl_info *info)
 	err = ovs_flow_metadata_from_nlattrs(flow, key_len, a[OVS_PACKET_ATTR_KEY]);
 	if (err)
 		goto err_flow_free;
-
-	err = validate_actions(a[OVS_PACKET_ATTR_ACTIONS], &flow->key, 0);
-	if (err)
-		goto err_flow_free;
-
-	acts = ovs_flow_actions_alloc(a[OVS_PACKET_ATTR_ACTIONS]);
+	acts = ovs_flow_actions_alloc(nla_len(a[OVS_PACKET_ATTR_ACTIONS]));
 	err = PTR_ERR(acts);
 	if (IS_ERR(acts))
 		goto err_flow_free;
+
+	err = validate_and_copy_actions(a[OVS_PACKET_ATTR_ACTIONS], &flow->key, 0, &acts);
 	rcu_assign_pointer(flow->sf_acts, acts);
+	if (err)
+		goto err_flow_free;
 
 	OVS_CB(packet)->flow = flow;
 	packet->priority = flow->key.phy.priority;
@@ -814,6 +947,98 @@ static struct genl_multicast_group ovs_dp_flow_multicast_group = {
 	.name = OVS_FLOW_MCGROUP
 };
 
+static int actions_to_attr(const struct nlattr *attr, int len, struct sk_buff *skb);
+static int sample_action_to_attr(const struct nlattr *attr, struct sk_buff *skb)
+{
+	const struct nlattr *a;
+	struct nlattr *start;
+	int err = 0, rem;
+
+	start = nla_nest_start(skb, OVS_ACTION_ATTR_SAMPLE);
+	if (!start)
+		return -EMSGSIZE;
+
+	nla_for_each_nested(a, attr, rem) {
+		int type = nla_type(a);
+		struct nlattr *st_sample;
+
+		switch (type) {
+		case OVS_SAMPLE_ATTR_PROBABILITY:
+			if (nla_put(skb, OVS_SAMPLE_ATTR_PROBABILITY, sizeof(u32), nla_data(a)))
+				return -EMSGSIZE;
+		break;
+		case OVS_SAMPLE_ATTR_ACTIONS:
+			st_sample = nla_nest_start(skb, OVS_SAMPLE_ATTR_ACTIONS);
+			if (!st_sample)
+				return -EMSGSIZE;
+			err = actions_to_attr(nla_data(a), nla_len(a), skb);
+			if (err)
+				return err;
+			nla_nest_end(skb, st_sample);
+		break;
+		}
+	}
+
+	nla_nest_end(skb, start);
+	return err;
+}
+
+static int set_tun_action_to_attr(const struct nlattr *a, struct sk_buff *skb, bool *set_tun)
+{
+	const struct nlattr *ovs_key = nla_data(a);
+	int key_type = nla_type(ovs_key);
+	struct nlattr *start;
+	int err;
+
+	switch (key_type) {
+	case OVS_KEY_ATTR_IPV4_TUNNEL:
+		*set_tun = true;
+		start = nla_nest_start(skb, OVS_ACTION_ATTR_SET);
+		if (!start)
+			return -EMSGSIZE;
+
+		err = ipv4_tun_to_nlattr(skb, nla_data(a));
+		if (err)
+			return err;
+		nla_nest_end(skb, start);
+		break;
+	}
+
+	return 0;
+}
+
+static int actions_to_attr(const struct nlattr *attr, int len, struct sk_buff *skb)
+{
+	const struct nlattr *a;
+	int rem, err;
+
+	nla_for_each_attr(a, attr, len, rem) {
+		bool skip_copy;
+		int type = nla_type(a);
+
+		skip_copy = false;
+		switch (type) {
+		case OVS_ACTION_ATTR_SET:
+			err = set_tun_action_to_attr(a, skb, &skip_copy);
+			if (err)
+				return err;
+			break;
+
+		case OVS_ACTION_ATTR_SAMPLE:
+			err = sample_action_to_attr(a, skb);
+			if (err)
+				return err;
+			skip_copy = true;
+			break;
+
+		}
+		if (!skip_copy && nla_put(skb, type, nla_len(a), nla_data(a)))
+			return -EMSGSIZE;
+	}
+
+	return 0;
+}
+
 /* Called with genl_lock. */
 static int ovs_flow_cmd_fill_info(struct sw_flow *flow, struct datapath *dp,
 				  struct sk_buff *skb, u32 portid,
@@ -821,6 +1046,7 @@ static int ovs_flow_cmd_fill_info(struct sw_flow *flow, struct datapath *dp,
 {
 	const int skb_orig_len = skb->len;
 	const struct sw_flow_actions *sf_acts;
+	struct nlattr *start;
 	struct ovs_flow_stats stats;
 	struct ovs_header *ovs_header;
 	struct nlattr *nla;
@@ -875,10 +1101,11 @@ static int ovs_flow_cmd_fill_info(struct sw_flow *flow, struct datapath *dp,
 	 * This can only fail for dump operations because the skb is always
 	 * properly sized for single flows.
 	 */
-	err = nla_put(skb, OVS_FLOW_ATTR_ACTIONS, sf_acts->actions_len,
-		      sf_acts->actions);
+	start = nla_nest_start(skb, OVS_FLOW_ATTR_ACTIONS);
+	err = actions_to_attr(sf_acts->actions, sf_acts->actions_len, skb);
 	if (err < 0 && skb_orig_len)
 		goto error;
+	nla_nest_end(skb, start);
 
 	return genlmsg_end(skb, ovs_header);
 
@@ -938,6 +1165,7 @@ static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, struct genl_info *info)
 	struct sk_buff *reply;
 	struct datapath *dp;
 	struct flow_table *table;
+	struct sw_flow_actions *acts = NULL;
 	int error;
 	int key_len;
 
@@ -951,28 +1179,32 @@ static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, struct genl_info *info)
 
 	/* Validate actions. */
 	if (a[OVS_FLOW_ATTR_ACTIONS]) {
-		error = validate_actions(a[OVS_FLOW_ATTR_ACTIONS], &key,  0);
-		if (error)
+		acts = ovs_flow_actions_alloc(nla_len(a[OVS_FLOW_ATTR_ACTIONS]));
+		error = PTR_ERR(acts);
+		if (IS_ERR(acts))
 			goto error;
+
+		error = validate_and_copy_actions(a[OVS_FLOW_ATTR_ACTIONS], &key,  0, &acts);
+		if (error) {
+			goto err_kfree;
+		}
 	} else if (info->genlhdr->cmd == OVS_FLOW_CMD_NEW) {
 		error = -EINVAL;
-		goto error;
+		goto err_kfree;
 	}
 
 	dp = get_dp(sock_net(skb->sk), ovs_header->dp_ifindex);
 	error = -ENODEV;
 	if (!dp)
-		goto error;
+		goto err_kfree;
 
 	table = genl_dereference(dp->table);
 	flow = ovs_flow_tbl_lookup(table, &key, key_len);
 	if (!flow) {
-		struct sw_flow_actions *acts;
-
 		/* Bail out if we're not allowed to create a new flow. */
 		error = -ENOENT;
 		if (info->genlhdr->cmd == OVS_FLOW_CMD_SET)
-			goto error;
+			goto err_kfree;
 
 		/* Expand table, if necessary, to make room. */
 		if (ovs_flow_tbl_need_to_expand(table)) {
@@ -990,15 +1222,10 @@ static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, struct genl_info *info)
 		flow = ovs_flow_alloc();
 		if (IS_ERR(flow)) {
 			error = PTR_ERR(flow);
-			goto error;
+			goto err_kfree;
 		}
 		clear_stats(flow);
 
-		/* Obtain actions. */
-		acts = ovs_flow_actions_alloc(a[OVS_FLOW_ATTR_ACTIONS]);
-		error = PTR_ERR(acts);
-		if (IS_ERR(acts))
-			goto error_free_flow;
 		rcu_assign_pointer(flow->sf_acts, acts);
 
 		/* Put flow in bucket. */
@@ -1010,7 +1237,6 @@ static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, struct genl_info *info)
 	} else {
 		/* We found a matching flow. */
 		struct sw_flow_actions *old_acts;
-		struct nlattr *acts_attrs;
 
 		/* Bail out if we're not allowed to modify an existing flow.
 		 * We accept NLM_F_CREATE in place of the intended NLM_F_EXCL
@@ -1021,26 +1247,13 @@ static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, struct genl_info *info)
 		error = -EEXIST;
 		if (info->genlhdr->cmd == OVS_FLOW_CMD_NEW &&
 		    info->nlhdr->nlmsg_flags & (NLM_F_CREATE | NLM_F_EXCL))
-			goto error;
+			goto err_kfree;
 
 		/* Update actions. */
 		old_acts = rcu_dereference_protected(flow->sf_acts,
 						     lockdep_genl_is_held());
-		acts_attrs = a[OVS_FLOW_ATTR_ACTIONS];
-		if (acts_attrs &&
-		   (old_acts->actions_len != nla_len(acts_attrs) ||
-		   memcmp(old_acts->actions, nla_data(acts_attrs),
-			  old_acts->actions_len))) {
-			struct sw_flow_actions *new_acts;
-
-			new_acts = ovs_flow_actions_alloc(acts_attrs);
-			error = PTR_ERR(new_acts);
-			if (IS_ERR(new_acts))
-				goto error;
-
-			rcu_assign_pointer(flow->sf_acts, new_acts);
-			ovs_flow_deferred_free_acts(old_acts);
-		}
+		rcu_assign_pointer(flow->sf_acts, acts);
+		ovs_flow_deferred_free_acts(old_acts);
 
 		reply = ovs_flow_cmd_build_info(flow, dp, info->snd_portid,
 					       info->snd_seq, OVS_FLOW_CMD_NEW);
@@ -1062,8 +1275,8 @@ static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, struct genl_info *info)
 				ovs_dp_flow_multicast_group.id,	PTR_ERR(reply));
 	return 0;
 
-error_free_flow:
-	ovs_flow_free(flow);
+err_kfree:
+	kfree(acts);
 error:
 	return error;
 }
diff --git a/datapath/flow.c b/datapath/flow.c
index 63eef77..49982f0 100644
--- a/datapath/flow.c
+++ b/datapath/flow.c
@@ -200,20 +200,18 @@ void ovs_flow_used(struct sw_flow *flow, struct sk_buff *skb)
 	spin_unlock(&flow->lock);
 }
 
-struct sw_flow_actions *ovs_flow_actions_alloc(const struct nlattr *actions)
+struct sw_flow_actions *ovs_flow_actions_alloc(int size)
 {
-	int actions_len = nla_len(actions);
 	struct sw_flow_actions *sfa;
 
-	if (actions_len > MAX_ACTIONS_BUFSIZE)
+	if (size > MAX_ACTIONS_BUFSIZE)
 		return ERR_PTR(-EINVAL);
 
-	sfa = kmalloc(sizeof(*sfa) + actions_len, GFP_KERNEL);
+	sfa = kmalloc(sizeof(*sfa) + size, GFP_KERNEL);
 	if (!sfa)
 		return ERR_PTR(-ENOMEM);
 
-	sfa->actions_len = actions_len;
-	memcpy(sfa->actions, nla_data(actions), actions_len);
+	sfa->actions_len = 0;
 	return sfa;
 }
 
@@ -848,7 +846,7 @@ const int ovs_key_lens[OVS_KEY_ATTR_MAX + 1] = {
 	[OVS_KEY_ATTR_ICMPV6] = sizeof(struct ovs_key_icmpv6),
 	[OVS_KEY_ATTR_ARP] = sizeof(struct ovs_key_arp),
 	[OVS_KEY_ATTR_ND] = sizeof(struct ovs_key_nd),
-	[OVS_KEY_ATTR_IPV4_TUNNEL] = sizeof(struct ovs_key_ipv4_tunnel),
+	[OVS_KEY_ATTR_TUNNEL] = -1,
 
 	/* Not upstream. */
 	[OVS_KEY_ATTR_TUN_ID] = sizeof(__be64),
@@ -989,6 +987,104 @@ static int parse_flow_nlattrs(const struct nlattr *attr,
 	return 0;
 }
 
+int ipv4_tun_from_nlattr(const struct nlattr *attr,
+			 struct ovs_key_ipv4_tunnel *tun_key)
+{
+	struct nlattr *a;
+	int rem;
+
+	memset(tun_key, 0, sizeof(*tun_key));
+
+	nla_for_each_nested(a, attr, rem) {
+		int type = nla_type(a);
+		static const u32 ovs_tunnel_key_lens[OVS_TUNNEL_MAX + 1] = {
+			[OVS_TUNNEL_ID] = sizeof(u64),
+			[OVS_TUNNEL_IPV4_SRC] = sizeof(u32),
+			[OVS_TUNNEL_IPV4_DST] = sizeof(u32),
+			[OVS_TUNNEL_TOS] = 1,
+			[OVS_TUNNEL_TTL] = 1,
+			[OVS_TUNNEL_FLAGS_DONT_FRAGMENT] = 0,
+			[OVS_TUNNEL_FLAGS_CSUM] = 0,
+		};
+
+		if (type > OVS_TUNNEL_MAX ||
+			ovs_tunnel_key_lens[type] != nla_len(a))
+			return -EINVAL;
+
+		switch (type) {
+		case OVS_TUNNEL_ID:
+			memcpy(&tun_key->tun_id, nla_data(a), sizeof(__be64));
+			tun_key->tun_flags |= OVS_TNL_F_KEY;
+		break;
+		case OVS_TUNNEL_IPV4_SRC:
+			memcpy(&tun_key->ipv4_src, nla_data(a), sizeof(__be32));
+		break;
+		case OVS_TUNNEL_IPV4_DST:
+			memcpy(&tun_key->ipv4_dst, nla_data(a), sizeof(__be32));
+		break;
+		case OVS_TUNNEL_TOS:
+			tun_key->ipv4_tos = nla_get_u8(a);
+		break;
+		case OVS_TUNNEL_TTL:
+			tun_key->ipv4_ttl = nla_get_u8(a);
+		break;
+		case OVS_TUNNEL_FLAGS_DONT_FRAGMENT:
+			tun_key->tun_flags |= OVS_TNL_F_DONT_FRAGMENT;
+		break;
+		case OVS_TUNNEL_FLAGS_CSUM:
+			tun_key->tun_flags |= OVS_TNL_F_CSUM;
+		break;
+		default:
+			return -EINVAL;
+
+		}
+	}
+	if (rem > 0)
+		return -EINVAL;
+
+	if (!tun_key->ipv4_dst)
+		return -EINVAL;
+
+	if (!tun_key->ipv4_ttl)
+		return -EINVAL;
+
+	return 0;
+}
+
+int ipv4_tun_to_nlattr(struct sk_buff *skb,
+			const struct ovs_key_ipv4_tunnel *tun_key)
+{
+	struct nlattr *nla;
+
+	nla = nla_nest_start(skb, OVS_KEY_ATTR_TUNNEL);
+	if (!nla)
+		return -EMSGSIZE;
+
+	if (tun_key->tun_flags & OVS_TNL_F_KEY &&
+	    nla_put_be64(skb, OVS_TUNNEL_ID, tun_key->tun_id))
+		return -EMSGSIZE;
+	if (tun_key->ipv4_src &&
+	    nla_put_be32(skb, OVS_TUNNEL_IPV4_SRC, tun_key->ipv4_src))
+		return -EMSGSIZE;
+	if (nla_put_be32(skb, OVS_TUNNEL_IPV4_DST, tun_key->ipv4_dst))
+		return -EMSGSIZE;
+	if (tun_key->ipv4_tos &&
+	    nla_put_u8(skb, OVS_TUNNEL_TOS, tun_key->ipv4_tos))
+		return -EMSGSIZE;
+	if (tun_key->ipv4_ttl &&
+	    nla_put_u8(skb, OVS_TUNNEL_TTL, tun_key->ipv4_ttl))
+		return -EMSGSIZE;
+	if ((tun_key->tun_flags & OVS_TNL_F_DONT_FRAGMENT) &&
+		nla_put_flag(skb, OVS_TUNNEL_FLAGS_DONT_FRAGMENT))
+		return -EMSGSIZE;
+	if ((tun_key->tun_flags & OVS_TNL_F_CSUM) &&
+		nla_put_flag(skb, OVS_TUNNEL_FLAGS_CSUM))
+		return -EMSGSIZE;
+
+	nla_nest_end(skb, nla);
+	return 0;
+}
+
 /**
  * ovs_flow_from_nlattrs - parses Netlink attributes into a flow key.
  * @swkey: receives the extracted flow key.
@@ -1037,37 +1133,29 @@ int ovs_flow_from_nlattrs(struct sw_flow_key *swkey, int *key_lenp,
 	}
 
 	if (attrs & (1ULL << OVS_KEY_ATTR_TUN_ID) &&
-	    attrs & (1ULL << OVS_KEY_ATTR_IPV4_TUNNEL)) {
-		struct ovs_key_ipv4_tunnel *tun_key;
+	    attrs & (1ULL << OVS_KEY_ATTR_TUNNEL)) {
 		__be64 tun_id;
 
-		tun_key = nla_data(a[OVS_KEY_ATTR_IPV4_TUNNEL]);
+		err = ipv4_tun_from_nlattr(a[OVS_KEY_ATTR_TUNNEL], &swkey->tun_key);
+		if (err)
+			return err;
 
-		if (!tun_key->ipv4_dst)
-			return -EINVAL;
-		if (!(tun_key->tun_flags & OVS_TNL_F_KEY))
+		if (!(swkey->tun_key.tun_flags & OVS_TNL_F_KEY))
 			return -EINVAL;
 
 		tun_id = nla_get_be64(a[OVS_KEY_ATTR_TUN_ID]);
-		if (tun_id != tun_key->tun_id)
+		if (tun_id != swkey->tun_key.tun_id)
 			return -EINVAL;
 
-		memcpy(&swkey->tun_key, tun_key, sizeof(swkey->tun_key));
-		memset(swkey->tun_key.pad, 0, sizeof(swkey->tun_key.pad));
-
 		attrs &= ~(1ULL << OVS_KEY_ATTR_TUN_ID);
-		attrs &= ~(1ULL << OVS_KEY_ATTR_IPV4_TUNNEL);
-	} else if (attrs & (1ULL << OVS_KEY_ATTR_IPV4_TUNNEL)) {
-		struct ovs_key_ipv4_tunnel *tun_key;
-		tun_key = nla_data(a[OVS_KEY_ATTR_IPV4_TUNNEL]);
+		attrs &= ~(1ULL << OVS_KEY_ATTR_TUNNEL);
+	} else if (attrs & (1ULL << OVS_KEY_ATTR_TUNNEL)) {
 
-		if (!tun_key->ipv4_dst)
-			return -EINVAL;
-
-		memcpy(&swkey->tun_key, tun_key, sizeof(swkey->tun_key));
-		memset(swkey->tun_key.pad, 0, sizeof(swkey->tun_key.pad));
+		err = ipv4_tun_from_nlattr(a[OVS_KEY_ATTR_TUNNEL], &swkey->tun_key);
+		if (err)
+			return err;
 
-		attrs &= ~(1ULL << OVS_KEY_ATTR_IPV4_TUNNEL);
+		attrs &= ~(1ULL << OVS_KEY_ATTR_TUNNEL);
 	}
 
 	/* Data attributes. */
@@ -1223,6 +1311,8 @@ int ovs_flow_metadata_from_nlattrs(struct sw_flow *flow, int key_len, const stru
 		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;
 
@@ -1246,21 +1336,23 @@ int ovs_flow_metadata_from_nlattrs(struct sw_flow *flow, int key_len, const stru
 
 				break;
 
-			case OVS_KEY_ATTR_IPV4_TUNNEL:
+			case OVS_KEY_ATTR_TUNNEL:
 				if (tun_key->tun_flags & OVS_TNL_F_KEY) {
 					tun_id = tun_key->tun_id;
+					err = ipv4_tun_from_nlattr(nla, tun_key);
+					if (err)
+						return err;
 
-					memcpy(tun_key, nla_data(nla), sizeof(*tun_key));
 					if (!(tun_key->tun_flags & OVS_TNL_F_KEY))
 						return -EINVAL;
 
 					if (tun_key->tun_id != tun_id)
 						return -EINVAL;
-				} else
-					memcpy(tun_key, nla_data(nla), sizeof(*tun_key));
-
-				if (!tun_key->ipv4_dst)
-					return -EINVAL;
+				} else {
+					err = ipv4_tun_from_nlattr(nla, tun_key);
+					if (err)
+						return err;
+				}
 				break;
 
 			case OVS_KEY_ATTR_IN_PORT:
@@ -1297,14 +1389,10 @@ int ovs_flow_to_nlattrs(const struct sw_flow_key *swkey, struct sk_buff *skb)
 	    nla_put_u32(skb, OVS_KEY_ATTR_PRIORITY, swkey->phy.priority))
 		goto nla_put_failure;
 
-	if (swkey->tun_key.ipv4_dst) {
-		struct ovs_key_ipv4_tunnel *tun_key;
-		nla = nla_reserve(skb, OVS_KEY_ATTR_IPV4_TUNNEL, sizeof(*tun_key));
-		if (!nla)
-			goto nla_put_failure;
-		tun_key = nla_data(nla);
-		memcpy(tun_key, &swkey->tun_key, sizeof(*tun_key));
-	}
+	if (swkey->tun_key.ipv4_dst &&
+	    ipv4_tun_to_nlattr(skb, &swkey->tun_key))
+		goto nla_put_failure;
+
 	if ((swkey->tun_key.tun_flags & OVS_TNL_F_KEY) &&
 	    nla_put_be64(skb, OVS_KEY_ATTR_TUN_ID, swkey->tun_key.tun_id))
 		goto nla_put_failure;
diff --git a/datapath/flow.h b/datapath/flow.h
index 3f3624f..4b43336 100644
--- a/datapath/flow.h
+++ b/datapath/flow.h
@@ -40,6 +40,16 @@ struct sw_flow_actions {
 	struct nlattr actions[];
 };
 
+struct ovs_key_ipv4_tunnel {
+	__be64 tun_id;
+	__u32  tun_flags;
+	__be32 ipv4_src;
+	__be32 ipv4_dst;
+	__u8   ipv4_tos;
+	__u8   ipv4_ttl;
+	__u8   pad[2];
+};
+
 struct sw_flow_key {
 	struct ovs_key_ipv4_tunnel tun_key;  /* Encapsulating tunnel key. */
 	struct {
@@ -131,7 +141,7 @@ struct sw_flow *ovs_flow_alloc(void);
 void ovs_flow_deferred_free(struct sw_flow *);
 void ovs_flow_free(struct sw_flow *);
 
-struct sw_flow_actions *ovs_flow_actions_alloc(const struct nlattr *);
+struct sw_flow_actions *ovs_flow_actions_alloc(int actions_len);
 void ovs_flow_deferred_free_acts(struct sw_flow_actions *);
 
 int ovs_flow_extract(struct sk_buff *, u16 in_port, struct sw_flow_key *,
@@ -203,5 +213,9 @@ 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);
+int ipv4_tun_to_nlattr(struct sk_buff *skb,
+			const struct ovs_key_ipv4_tunnel *tun_key);
 
 #endif /* flow.h */
diff --git a/datapath/tunnel.h b/datapath/tunnel.h
index 7705475..809fefd 100644
--- a/datapath/tunnel.h
+++ b/datapath/tunnel.h
@@ -59,6 +59,11 @@
 		      TNL_F_DF_INHERIT | TNL_F_DF_DEFAULT | TNL_F_PMTUD | \
 		      TNL_F_IPSEC)
 
+/* Tunnel flow flags. */
+#define OVS_TNL_F_DONT_FRAGMENT		(1 << 0)
+#define OVS_TNL_F_CSUM			(1 << 1)
+#define OVS_TNL_F_KEY			(1 << 2)
+
 /**
  * struct port_lookup_key - Tunnel port key, used as hash table key.
  * @in_key: Key to match on input, 0 for wildcard.
diff --git a/include/linux/openvswitch.h b/include/linux/openvswitch.h
index 5e32965..9b4e257 100644
--- a/include/linux/openvswitch.h
+++ b/include/linux/openvswitch.h
@@ -282,13 +282,29 @@ enum ovs_key_attr {
 	OVS_KEY_ATTR_ARP,       /* struct ovs_key_arp */
 	OVS_KEY_ATTR_ND,        /* struct ovs_key_nd */
 	OVS_KEY_ATTR_SKB_MARK,  /* u32 skb mark */
+	OVS_KEY_ATTR_TUNNEL,	/* Nested set of ovs_tunnel attributes */
+
+#ifdef __KERNEL__
 	OVS_KEY_ATTR_IPV4_TUNNEL,  /* struct ovs_key_ipv4_tunnel */
+#endif
 	OVS_KEY_ATTR_TUN_ID = 63,  /* be64 tunnel ID */
 	__OVS_KEY_ATTR_MAX
 };
 
 #define OVS_KEY_ATTR_MAX (__OVS_KEY_ATTR_MAX - 1)
 
+enum ovs_tunnel_attr {
+	OVS_TUNNEL_ID,		/* be64 Tunnel ID */
+	OVS_TUNNEL_IPV4_SRC,	/* be32 Tunnel src IP address. */
+	OVS_TUNNEL_IPV4_DST,	/* be32 Tunnel dst IP address. */
+	OVS_TUNNEL_TOS,		/* u8 Tunnel IP ToS. */
+	OVS_TUNNEL_TTL,		/* u8 Tunnel IP TTL. */
+	OVS_TUNNEL_FLAGS_DONT_FRAGMENT, /* No argument, flag to set DF. */
+	OVS_TUNNEL_FLAGS_CSUM,	/* No argument. flag to CSUM packet. */
+	__OVS_TUNNEL_MAX
+};
+#define OVS_TUNNEL_MAX (__OVS_TUNNEL_MAX - 1)
+
 /**
  * enum ovs_frag_type - IPv4 and IPv6 fragment type
  * @OVS_FRAG_TYPE_NONE: Packet is not a fragment.
@@ -365,21 +381,6 @@ struct ovs_key_nd {
 	__u8  nd_tll[6];
 };
 
-/* Values for ovs_key_ipv4_tunnel->tun_flags */
-#define OVS_TNL_F_DONT_FRAGMENT (1 << 0)
-#define OVS_TNL_F_CSUM (1 << 1)
-#define OVS_TNL_F_KEY (1 << 2)
-
-struct ovs_key_ipv4_tunnel {
-	__be64 tun_id;
-	__u32  tun_flags;
-	__be32 ipv4_src;
-	__be32 ipv4_dst;
-	__u8   ipv4_tos;
-	__u8   ipv4_ttl;
-	__u8   pad[2];
-};
-
 /**
  * enum ovs_flow_attr - attributes for %OVS_FLOW_* commands.
  * @OVS_FLOW_ATTR_KEY: Nested %OVS_KEY_ATTR_* attributes specifying the flow
diff --git a/lib/dpif-netdev.c b/lib/dpif-netdev.c
index 682dfc9..67a5333 100644
--- a/lib/dpif-netdev.c
+++ b/lib/dpif-netdev.c
@@ -1193,7 +1193,7 @@ execute_set_action(struct ofpbuf *packet, const struct nlattr *a)
     case OVS_KEY_ATTR_TUN_ID:
     case OVS_KEY_ATTR_PRIORITY:
     case OVS_KEY_ATTR_SKB_MARK:
-    case OVS_KEY_ATTR_IPV4_TUNNEL:
+    case OVS_KEY_ATTR_TUNNEL:
         /* not implemented */
         break;
 
diff --git a/lib/odp-util.c b/lib/odp-util.c
index e2f21da..5d7f25a 100644
--- a/lib/odp-util.c
+++ b/lib/odp-util.c
@@ -95,7 +95,7 @@ ovs_key_attr_to_string(enum ovs_key_attr attr)
     case OVS_KEY_ATTR_PRIORITY: return "skb_priority";
     case OVS_KEY_ATTR_SKB_MARK: return "skb_mark";
     case OVS_KEY_ATTR_TUN_ID: return "tun_id";
-    case OVS_KEY_ATTR_IPV4_TUNNEL: return "ipv4_tunnel";
+    case OVS_KEY_ATTR_TUNNEL: return "tunnel";
     case OVS_KEY_ATTR_IN_PORT: return "in_port";
     case OVS_KEY_ATTR_ETHERNET: return "eth";
     case OVS_KEY_ATTR_VLAN: return "vlan";
@@ -617,7 +617,7 @@ odp_flow_key_attr_len(uint16_t type)
     case OVS_KEY_ATTR_PRIORITY: return 4;
     case OVS_KEY_ATTR_SKB_MARK: return 4;
     case OVS_KEY_ATTR_TUN_ID: return 8;
-    case OVS_KEY_ATTR_IPV4_TUNNEL: return sizeof(struct ovs_key_ipv4_tunnel);
+    case OVS_KEY_ATTR_TUNNEL: return -2;
     case OVS_KEY_ATTR_IN_PORT: return 4;
     case OVS_KEY_ATTR_ETHERNET: return sizeof(struct ovs_key_ethernet);
     case OVS_KEY_ATTR_VLAN: return sizeof(ovs_be16);
@@ -672,19 +672,104 @@ ovs_frag_type_to_string(enum ovs_frag_type type)
     }
 }
 
-static const char *
-odp_tun_flag_to_string(uint32_t flags)
+static int
+tunnel_key_attr_len(int type)
 {
-    switch (flags) {
-    case OVS_TNL_F_DONT_FRAGMENT:
-        return "df";
-    case OVS_TNL_F_CSUM:
-        return "csum";
-    case OVS_TNL_F_KEY:
-        return "key";
-    default:
-        return NULL;
+    switch (type) {
+    case OVS_TUNNEL_ID: return 8;
+    case OVS_TUNNEL_IPV4_SRC: return 4;
+    case OVS_TUNNEL_IPV4_DST: return 4;
+    case OVS_TUNNEL_TOS: return 1;
+    case OVS_TUNNEL_TTL: return 1;
+    case OVS_TUNNEL_FLAGS_DONT_FRAGMENT: return 0;
+    case OVS_TUNNEL_FLAGS_CSUM: return 0;
+    case __OVS_TUNNEL_MAX:
+        return -1;
     }
+    return -1;
+}
+
+/* Returns OVS_TNL_* flags. */
+static enum odp_key_fitness
+tun_key_from_attr(const struct nlattr *attr, struct flow_tnl *tun)
+{
+    unsigned int left;
+    const struct nlattr *a;
+
+    NL_NESTED_FOR_EACH(a, left, attr) {
+        uint16_t type = nl_attr_type(a);
+        size_t len = nl_attr_get_size(a);
+        int expected_len = tunnel_key_attr_len(type);
+
+        if (len != expected_len && expected_len >= 0) {
+            return ODP_FIT_ERROR;
+        }
+
+        switch (type) {
+        case OVS_TUNNEL_ID:
+            tun->tun_id = nl_attr_get_be64(a);
+            tun->flags |= FLOW_TNL_F_KEY;
+        break;
+        case OVS_TUNNEL_IPV4_SRC:
+            tun->ip_src = nl_attr_get_be32(a);
+        break;
+        case OVS_TUNNEL_IPV4_DST:
+            tun->ip_dst = nl_attr_get_be32(a);
+        break;
+        case OVS_TUNNEL_TOS:
+            tun->ip_tos = nl_attr_get_u8(a);
+        break;
+        case OVS_TUNNEL_TTL:
+            tun->ip_ttl = nl_attr_get_u8(a);
+        break;
+        case OVS_TUNNEL_FLAGS_DONT_FRAGMENT:
+            tun->flags |= FLOW_TNL_F_DONT_FRAGMENT;
+        break;
+        case OVS_TUNNEL_FLAGS_CSUM:
+            tun->flags |= FLOW_TNL_F_CSUM;
+        break;
+        default:
+            return ODP_FIT_TOO_MUCH;
+        break;
+        }
+    }
+
+    return ODP_FIT_PERFECT;
+}
+
+static int
+tun_key_to_attr(struct ofpbuf *a, const struct flow_tnl *tun_key)
+{
+    size_t tun_key_ofs;
+
+    tun_key_ofs = nl_msg_start_nested(a, OVS_KEY_ATTR_TUNNEL);
+
+    if (tun_key->flags & FLOW_TNL_F_KEY) {
+        nl_msg_put_be64(a, OVS_TUNNEL_ID, tun_key->tun_id);
+    }
+    if (tun_key->ip_src) {
+        nl_msg_put_be32(a, OVS_TUNNEL_IPV4_SRC, tun_key->ip_src);
+    }
+    if (tun_key->ip_dst) {
+        nl_msg_put_be32(a, OVS_TUNNEL_IPV4_DST, tun_key->ip_dst);
+    }
+    if (tun_key->ip_tos) {
+        nl_msg_put_u8(a, OVS_TUNNEL_TOS, tun_key->ip_tos);
+    }
+    if (tun_key->ip_ttl) {
+        nl_msg_put_u8(a, OVS_TUNNEL_TTL, tun_key->ip_ttl);
+    } else {
+        return -EINVAL;
+    }
+    if (tun_key->flags & FLOW_TNL_F_DONT_FRAGMENT) {
+        nl_msg_put_flag(a, OVS_TUNNEL_FLAGS_DONT_FRAGMENT);
+    }
+    if (tun_key->flags & FLOW_TNL_F_CSUM) {
+        nl_msg_put_flag(a, OVS_TUNNEL_FLAGS_CSUM);
+    }
+
+    nl_msg_end_nested(a, tun_key_ofs);
+    return 0;
 }
 
 static void
@@ -699,7 +784,7 @@ format_odp_key_attr(const struct nlattr *a, struct ds *ds)
     const struct ovs_key_icmpv6 *icmpv6_key;
     const struct ovs_key_arp *arp_key;
     const struct ovs_key_nd *nd_key;
-    const struct ovs_key_ipv4_tunnel *ipv4_tun_key;
+    struct flow_tnl tun_key;
     enum ovs_key_attr attr = nl_attr_type(a);
     int expected_len;
 
@@ -734,18 +819,22 @@ format_odp_key_attr(const struct nlattr *a, struct ds *ds)
         ds_put_format(ds, "(%#"PRIx64")", ntohll(nl_attr_get_be64(a)));
         break;
 
-    case OVS_KEY_ATTR_IPV4_TUNNEL:
-        ipv4_tun_key = nl_attr_get(a);
-        ds_put_format(ds, "(tun_id=0x%"PRIx64",src="IP_FMT",dst="IP_FMT","
-                      "tos=0x%"PRIx8",ttl=%"PRIu8",flags(",
-                      ntohll(ipv4_tun_key->tun_id),
-                      IP_ARGS(ipv4_tun_key->ipv4_src),
-                      IP_ARGS(ipv4_tun_key->ipv4_dst),
-                      ipv4_tun_key->ipv4_tos, ipv4_tun_key->ipv4_ttl);
-
-        format_flags(ds, odp_tun_flag_to_string,
-                     ipv4_tun_key->tun_flags, ',');
-        ds_put_format(ds, "))");
+    case OVS_KEY_ATTR_TUNNEL:
+        memset(&tun_key, 0, sizeof tun_key);
+        if (tun_key_from_attr(a, &tun_key) == ODP_FIT_ERROR) {
+            ds_put_format(ds, "(error)");
+        } else {
+            ds_put_format(ds, "(tun_id=0x%"PRIx64",src="IP_FMT",dst="IP_FMT","
+                          "tos=0x%"PRIx8",ttl=%"PRIu8",flags(",
+                          ntohll(tun_key.tun_id),
+                          IP_ARGS(tun_key.ip_src),
+                          IP_ARGS(tun_key.ip_dst),
+                          tun_key.ip_tos, tun_key.ip_ttl);
+
+            format_flags(ds, flow_tun_flag_to_string,
+                         (uint32_t) tun_key.flags, ',');
+            ds_put_format(ds, "))");
+        }
         break;
 
     case OVS_KEY_ATTR_IN_PORT:
@@ -974,23 +1063,24 @@ parse_odp_key_attr(const char *s, const struct simap *port_names,
     {
         char tun_id_s[32];
         int tos, ttl;
-        struct ovs_key_ipv4_tunnel tun_key;
+        struct flow_tnl tun_key;
         int n = -1;
 
-        if (sscanf(s, "ipv4_tunnel(tun_id=%31[x0123456789abcdefABCDEF],"
+        if (sscanf(s, "tunnel(tun_id=%31[x0123456789abcdefABCDEF],"
                    "src="IP_SCAN_FMT",dst="IP_SCAN_FMT
                    ",tos=%i,ttl=%i,flags%n", tun_id_s,
-                    IP_SCAN_ARGS(&tun_key.ipv4_src),
-                    IP_SCAN_ARGS(&tun_key.ipv4_dst), &tos, &ttl,
+                    IP_SCAN_ARGS(&tun_key.ip_src),
+                    IP_SCAN_ARGS(&tun_key.ip_dst), &tos, &ttl,
                     &n) > 0 && n > 0) {
             int res;
+            uint32_t flags;
 
             tun_key.tun_id = htonll(strtoull(tun_id_s, NULL, 0));
-            tun_key.ipv4_tos = tos;
-            tun_key.ipv4_ttl = ttl;
+            tun_key.ip_tos = tos;
+            tun_key.ip_ttl = ttl;
+            res = parse_flags(&s[n], flow_tun_flag_to_string, &flags);
+            tun_key.flags = (uint16_t) flags;
 
-            res = parse_flags(&s[n], odp_tun_flag_to_string,
-                              &tun_key.tun_flags);
             if (res < 0) {
                 return res;
             }
@@ -999,10 +1089,9 @@ parse_odp_key_attr(const char *s, const struct simap *port_names,
                 return -EINVAL;
             }
             n++;
-
-            memset(&tun_key.pad, 0, sizeof tun_key.pad);
-            nl_msg_put_unspec(key, OVS_KEY_ATTR_IPV4_TUNNEL, &tun_key,
-                              sizeof tun_key);
+            if (tun_key_to_attr(key, &tun_key)) {
+                return -1;
+            }
             return n;
         }
     }
@@ -1342,27 +1431,6 @@ ovs_to_odp_frag(uint8_t nw_frag)
           : OVS_FRAG_TYPE_LATER);
 }
 
-/* The set of kernel flags we understand. Used to detect if ODP_FIT_TOO_MUCH */
-#define OVS_TNL_F_KNOWN_MASK \
-    (OVS_TNL_F_DONT_FRAGMENT | OVS_TNL_F_CSUM | OVS_TNL_F_KEY)
-
-/* These allow the flow/kernel view of the flags to change in future */
-static uint32_t
-flow_to_odp_flags(uint16_t flags)
-{
-    return (flags & FLOW_TNL_F_DONT_FRAGMENT ? OVS_TNL_F_DONT_FRAGMENT : 0)
-        | (flags & FLOW_TNL_F_CSUM ? OVS_TNL_F_CSUM : 0)
-        | (flags & FLOW_TNL_F_KEY ? OVS_TNL_F_KEY : 0);
-}
-
-static uint16_t
-odp_to_flow_flags(uint32_t tun_flags)
-{
-    return (tun_flags & OVS_TNL_F_DONT_FRAGMENT ? FLOW_TNL_F_DONT_FRAGMENT : 0)
-        | (tun_flags & OVS_TNL_F_CSUM ? FLOW_TNL_F_CSUM : 0)
-        | (tun_flags & OVS_TNL_F_KEY ? FLOW_TNL_F_KEY : 0);
-}
-
 /* Appends a representation of 'flow' as OVS_KEY_ATTR_* attributes to 'buf'.
  * 'flow->in_port' is ignored (since it is likely to be an OpenFlow port
  * number rather than a datapath port number).  Instead, if 'odp_in_port'
@@ -1383,18 +1451,7 @@ odp_flow_key_from_flow(struct ofpbuf *buf, const struct flow *flow,
     }
 
     if (flow->tunnel.ip_dst) {
-        struct ovs_key_ipv4_tunnel *ipv4_tun_key;
-
-        ipv4_tun_key = nl_msg_put_unspec_uninit(buf, OVS_KEY_ATTR_IPV4_TUNNEL,
-                                            sizeof *ipv4_tun_key);
-        /* layouts differ, flags has different size */
-        ipv4_tun_key->tun_id = flow->tunnel.tun_id;
-        ipv4_tun_key->tun_flags = flow_to_odp_flags(flow->tunnel.flags);
-        ipv4_tun_key->ipv4_src = flow->tunnel.ip_src;
-        ipv4_tun_key->ipv4_dst = flow->tunnel.ip_dst;
-        ipv4_tun_key->ipv4_tos = flow->tunnel.ip_tos;
-        ipv4_tun_key->ipv4_ttl = flow->tunnel.ip_ttl;
-        memset(ipv4_tun_key->pad, 0, sizeof ipv4_tun_key->pad);
+        tun_key_to_attr(buf, &flow->tunnel);
     } else if (flow->tunnel.tun_id != htonll(0)) {
         nl_msg_put_be64(buf, OVS_KEY_ATTR_TUN_ID, flow->tunnel.tun_id);
     }
@@ -1905,23 +1962,17 @@ odp_flow_key_to_flow(const struct nlattr *key, size_t key_len,
         expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_TUN_ID;
     }
 
-    if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_IPV4_TUNNEL)) {
-        const struct ovs_key_ipv4_tunnel *ipv4_tun_key;
-
-        ipv4_tun_key = nl_attr_get(attrs[OVS_KEY_ATTR_IPV4_TUNNEL]);
-
-        flow->tunnel.tun_id = ipv4_tun_key->tun_id;
-        flow->tunnel.ip_src = ipv4_tun_key->ipv4_src;
-        flow->tunnel.ip_dst = ipv4_tun_key->ipv4_dst;
-        flow->tunnel.flags = odp_to_flow_flags(ipv4_tun_key->tun_flags);
-        flow->tunnel.ip_tos = ipv4_tun_key->ipv4_tos;
-        flow->tunnel.ip_ttl = ipv4_tun_key->ipv4_ttl;
+    if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_TUNNEL)) {
+        enum odp_key_fitness res;
 
+        res = tun_key_from_attr(attrs[OVS_KEY_ATTR_TUNNEL], &flow->tunnel);
+        if (res == ODP_FIT_ERROR) {
+            return ODP_FIT_ERROR;
+        } else if (res == ODP_FIT_PERFECT) {
         /* Allow this to show up as unexpected, if there are unknown flags,
          * eventually resulting in ODP_FIT_TOO_MUCH.
          * OVS_TNL_F_KNOWN_MASK defined locally above. */
-        if (!(ipv4_tun_key->tun_flags & ~OVS_TNL_F_KNOWN_MASK)) {
-            expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_IPV4_TUNNEL;
+            expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_TUNNEL;
         }
     }
 
@@ -2017,18 +2068,11 @@ commit_set_tunnel_action(const struct flow *flow, struct flow *base,
 
     /* A valid IPV4_TUNNEL must have non-zero ip_dst. */
     if (flow->tunnel.ip_dst) {
-        struct ovs_key_ipv4_tunnel ipv4_tun_key;
-
-        ipv4_tun_key.tun_id = base->tunnel.tun_id;
-        ipv4_tun_key.tun_flags = flow_to_odp_flags(base->tunnel.flags);
-        ipv4_tun_key.ipv4_src = base->tunnel.ip_src;
-        ipv4_tun_key.ipv4_dst = base->tunnel.ip_dst;
-        ipv4_tun_key.ipv4_tos = base->tunnel.ip_tos;
-        ipv4_tun_key.ipv4_ttl = base->tunnel.ip_ttl;
-        memset(&ipv4_tun_key.pad, 0, sizeof ipv4_tun_key.pad);
-
-        commit_set_action(odp_actions, OVS_KEY_ATTR_IPV4_TUNNEL,
-                          &ipv4_tun_key, sizeof ipv4_tun_key);
+        size_t offset;
+
+        offset = nl_msg_start_nested(odp_actions, OVS_ACTION_ATTR_SET);
+        tun_key_to_attr(odp_actions, &base->tunnel);
+        nl_msg_end_nested(odp_actions, offset);
     } else {
         commit_set_action(odp_actions, OVS_KEY_ATTR_TUN_ID,
                           &base->tunnel.tun_id, sizeof base->tunnel.tun_id);
diff --git a/tests/odp.at b/tests/odp.at
index 009ac36..687f9c9 100644
--- a/tests/odp.at
+++ b/tests/odp.at
@@ -32,7 +32,7 @@ skb_mark(0x1234),in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth
 
  echo
  echo '# Valid forms with tunnel header.'
- sed 's/^/ipv4_tunnel(tun_id=0x7f10354,src=10.10.10.10,dst=20.20.20.20,tos=0x0,ttl=64,flags(csum,key)),/' odp-base.txt
+ sed 's/^/tunnel(tun_id=0x7f10354,src=10.10.10.10,dst=20.20.20.20,tos=0x0,ttl=64,flags(csum,key)),/' odp-base.txt
 
  echo
  echo '# Valid forms with VLAN header.'
@@ -45,13 +45,13 @@ s/$/)/' odp-base.txt
 
  echo
  echo '# Valid forms with tunnel and VLAN headers.'
- sed 's/^/ipv4_tunnel(tun_id=0xfedcba9876543210,src=10.0.0.1,dst=10.0.0.2,tos=0x8,ttl=128,flags()),/
+ sed 's/^/tunnel(tun_id=0xfedcba9876543210,src=10.0.0.1,dst=10.0.0.2,tos=0x8,ttl=128,flags(key)),/
 s/\(eth([[^)]]*)\),*/\1,eth_type(0x8100),vlan(vid=99,pcp=7),encap(/
 s/$/)/' odp-base.txt
 
  echo
  echo '# Valid forms with QOS priority, tunnel, and VLAN headers.'
- sed 's/^/skb_priority(0x1234),ipv4_tunnel(tun_id=0xfedcba9876543210,src=10.10.10.10,dst=20.20.20.20,tos=0x8,ttl=64,flags(key)),/
+ sed 's/^/skb_priority(0x1234),tunnel(tun_id=0xfedcba9876543210,src=10.10.10.10,dst=20.20.20.20,tos=0x8,ttl=64,flags(key)),/
 s/\(eth([[^)]]*)\),*/\1,eth_type(0x8100),vlan(vid=99,pcp=7),encap(/
 s/$/)/' odp-base.txt
 
@@ -92,9 +92,8 @@ push_vlan(tpid=0x9100,vid=13,pcp=5)
 push_vlan(tpid=0x9100,vid=13,pcp=5,cfi=0)
 pop_vlan
 sample(sample=9.7%,actions(1,2,3,push_vlan(vid=1,pcp=2)))
-set(ipv4_tunnel(tun_id=0xabcdef1234567890,src=1.1.1.1,dst=2.2.2.2,tos=0x0,ttl=64,flags(df,csum,key)))
-set(ipv4_tunnel(tun_id=0xabcdef1234567890,src=1.1.1.1,dst=2.2.2.2,tos=0x0,ttl=64,flags(df,csum,key,0x20)))
-set(ipv4_tunnel(tun_id=0xabcdef1234567890,src=1.1.1.1,dst=2.2.2.2,tos=0x0,ttl=64,flags()))
+set(tunnel(tun_id=0xabcdef1234567890,src=1.1.1.1,dst=2.2.2.2,tos=0x0,ttl=64,flags(df,csum,key)))
+set(tunnel(tun_id=0xabcdef1234567890,src=1.1.1.1,dst=2.2.2.2,tos=0x0,ttl=64,flags(key)))
 ])
 AT_CHECK_UNQUOTED([test-odp parse-actions < actions.txt], [0],
   [`cat actions.txt`
-- 
1.7.10




More information about the dev mailing list