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 <pshe...@nicira.com>
Bug #14611
---
 datapath/datapath.c         |  144 +++++++++++++++++++----------
 datapath/flow.c             |  141 +++++++++++++++++++++--------
 datapath/flow.h             |   13 +++
 datapath/tunnel.h           |    1 -
 include/linux/openvswitch.h |   36 ++++----
 lib/dpif-netdev.c           |    2 +-
 lib/flow.c                  |    1 -
 lib/flow.h                  |    4 +-
 lib/odp-util.c              |  211 +++++++++++++++++++++++++++----------------
 tests/test-bundle.c         |    1 -
 tests/test-multipath.c      |    1 -
 11 files changed, 373 insertions(+), 182 deletions(-)

diff --git a/datapath/datapath.c b/datapath/datapath.c
index 30e26a7..4ed00cf 100644
--- a/datapath/datapath.c
+++ b/datapath/datapath.c
@@ -423,11 +423,13 @@ 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 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_sample(const struct nlattr *attr,
-                               const struct sw_flow_key *key, int depth)
+                          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;
@@ -451,7 +453,7 @@ 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);
+       return validate_and_copy_actions(actions, key, depth + 1, sfa);
 }
 
 static int validate_tp_port(const struct sw_flow_key *flow_key)
@@ -467,8 +469,41 @@ static int validate_tp_port(const struct sw_flow_key 
*flow_key)
        return -EINVAL;
 }
 
+static int ____nla_put(struct sw_flow_actions *sfa, int attrtype, void *data, 
int len)
+{
+       struct nlattr *a = (struct nlattr *) ((unsigned char *)sfa->actions + 
sfa->used);
+
+       if (NLA_ALIGN(nla_attr_size(len)) > (sfa->actions_len - sfa->used));
+               return -EMSGSIZE;
+
+       a->nla_type = attrtype;
+       a->nla_len = nla_attr_size(len);
+
+       memset((unsigned char *) a + a->nla_len, 0, nla_padlen(len));
+       memcpy(nla_data(a), data, len);
+       sfa->used += NLA_ALIGN(nla_attr_size(len));
+}
+
+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;
+
+       err = ipv4_tun_from_nlattr(attr, &tun_key);
+       if (err)
+               return err;
+
+       err = ____nla_put(sfa, OVS_KEY_ATTR_IPV4_TUNNEL, &tun_key, 
sizeof(tun_key));
+       if (err)
+               return err;
+       return 0;
+}
+
 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 +513,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 +534,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 +616,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_attr(const struct nlattr *from,
+                     struct sw_flow_actions *sfa)
+{
+       unsigned char *to = (unsigned char *)sfa->actions + sfa->used;
+       int totlen = NLA_ALIGN(from->nla_len);
+
+       if (totlen > (sfa->actions_len - sfa->used))
+               return -EMSGSIZE;
+
+       memcpy(to, from, totlen);
+       sfa->used += 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 +653,16 @@ static int validate_actions(const struct nlattr *attr,
                };
                const struct ovs_action_push_vlan *vlan;
                int type = nla_type(a);
+               bool set_tun;
+
 
                if (type > OVS_ACTION_ATTR_MAX ||
                    (action_lens[type] != nla_len(a) &&
                     action_lens[type] != (u32)-1))
                        return -EINVAL;
 
+               set_tun = false;
+
                switch (type) {
                case OVS_ACTION_ATTR_UNSPEC:
                        return -EINVAL;
@@ -634,13 +691,13 @@ 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, &set_tun);
                        if (err)
                                return err;
                        break;
 
                case OVS_ACTION_ATTR_SAMPLE:
-                       err = validate_sample(a, key, depth);
+                       err = validate_sample(a, key, depth, sfa);
                        if (err)
                                return err;
                        break;
@@ -648,6 +705,11 @@ static int validate_actions(const struct nlattr *attr,
                default:
                        return -EINVAL;
                }
+               if (!set_tun) {
+                       err = copy_attr(a, sfa);
+                       if (err)
+                               return err;
+               }
        }
 
        if (rem > 0)
@@ -716,17 +778,16 @@ 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]);
        err = PTR_ERR(acts);
        if (IS_ERR(acts))
                goto err_flow_free;
        rcu_assign_pointer(flow->sf_acts, acts);
 
+       err = validate_and_copy_actions(a[OVS_PACKET_ATTR_ACTIONS], &flow->key, 
0, acts);
+       if (err)
+               goto err_flow_free;
+
        OVS_CB(packet)->flow = flow;
        packet->priority = flow->key.phy.priority;
        skb_set_mark(packet, flow->key.phy.skb_mark);
@@ -938,6 +999,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,9 +1013,16 @@ 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(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) {
+                       ovs_flow_deferred_free_acts(acts);
                        goto error;
+               }
        } else if (info->genlhdr->cmd == OVS_FLOW_CMD_NEW) {
                error = -EINVAL;
                goto error;
@@ -967,8 +1036,6 @@ static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, 
struct genl_info *info)
        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)
@@ -994,11 +1061,6 @@ static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, 
struct genl_info *info)
                }
                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 +1072,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
@@ -1026,21 +1087,14 @@ static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, 
struct genl_info *info)
                /* 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);
+               if (acts &&
+                  (old_acts->actions_len != acts->actions_len ||
+                   memcmp(old_acts->actions, acts->actions,
+                          old_acts->actions_len))) {
+                       rcu_assign_pointer(flow->sf_acts, acts);
                        ovs_flow_deferred_free_acts(old_acts);
-               }
+               } else
+                       ovs_flow_deferred_free_acts(acts);
 
                reply = ovs_flow_cmd_build_info(flow, dp, info->snd_portid,
                                               info->snd_seq, OVS_FLOW_CMD_NEW);
@@ -1062,8 +1116,6 @@ 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);
 error:
        return error;
 }
diff --git a/datapath/flow.c b/datapath/flow.c
index 63eef77..ce563f3 100644
--- a/datapath/flow.c
+++ b/datapath/flow.c
@@ -213,7 +213,7 @@ struct sw_flow_actions *ovs_flow_actions_alloc(const struct 
nlattr *actions)
                return ERR_PTR(-ENOMEM);
 
        sfa->actions_len = actions_len;
-       memcpy(sfa->actions, nla_data(actions), actions_len);
+       sfa->used = 0;
        return sfa;
 }
 
@@ -848,7 +848,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 +989,81 @@ 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);
+
+               switch (type) {
+               case OVS_KEY_ATTR_TUN_ID:
+                       memcpy(&tun_key->tun_id, nla_data(a), sizeof(__be64));
+               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:
+                       tun_key->tun_flags = nla_get_u32(a);
+               break;
+               default:
+                       return -EINVAL;
+
+               }
+       }
+       if (rem > 0)
+               return -EINVAL;
+
+       if (!tun_key->ipv4_dst)
+               return -EINVAL;
+
+       return 0;
+}
+
+static 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_KEY_ATTR_TUN_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 (tun_key->ipv4_dst &&
+           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 &&
+           nla_put_u32(skb, OVS_TUNNEL_FLAGS, tun_key->tun_flags))
+               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 +1112,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]);
-
-               if (!tun_key->ipv4_dst)
-                       return -EINVAL;
+               attrs &= ~(1ULL << OVS_KEY_ATTR_TUNNEL);
+       } else if (attrs & (1ULL << OVS_KEY_ATTR_TUNNEL)) {
 
-               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 +1290,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 +1315,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 +1368,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..ae15024 100644
--- a/datapath/flow.h
+++ b/datapath/flow.h
@@ -37,9 +37,20 @@ struct sk_buff;
 struct sw_flow_actions {
        struct rcu_head rcu;
        u32 actions_len;
+       u32 used;
        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 {
@@ -203,5 +214,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);
 
 #endif /* flow.h */
diff --git a/datapath/tunnel.h b/datapath/tunnel.h
index 7705475..b7de7a9 100644
--- a/datapath/tunnel.h
+++ b/datapath/tunnel.h
@@ -201,7 +201,6 @@ static inline void tnl_tun_key_init(struct 
ovs_key_ipv4_tunnel *tun_key,
        tun_key->ipv4_tos = iph->tos;
        tun_key->ipv4_ttl = iph->ttl;
        tun_key->tun_flags = tun_flags;
-       memset(tun_key->pad, 0, sizeof(tun_key->pad));
 }
 
 static inline void tnl_get_param(const struct tnl_mutable_config *mutable,
diff --git a/include/linux/openvswitch.h b/include/linux/openvswitch.h
index 5e32965..96f8bcc 100644
--- a/include/linux/openvswitch.h
+++ b/include/linux/openvswitch.h
@@ -265,6 +265,21 @@ struct ovs_flow_stats {
        __u64 n_bytes;           /* Number of matched bytes. */
 };
 
+
+/* Values for OVS_TUNNEL_FLAGS */
+#define OVS_TNL_F_DONT_FRAGMENT (1 << 0)
+#define OVS_TNL_F_CSUM (1 << 1)
+#define OVS_TNL_F_KEY (1 << 2)
+
+enum ovs_tunnel {
+       OVS_TUNNEL_IPV4_SRC,    /* Tunnel src ip address. */
+       OVS_TUNNEL_IPV4_DST,    /* Tunnel dst ip address. */
+       OVS_TUNNEL_TOS,         /* Tunnel ip TOS. */
+       OVS_TUNNEL_TTL,         /* Tunnel ip TTL. */
+       OVS_TUNNEL_FLAGS,       /* OVS_TNL_F_* flags. */
+       __OVS_TUNNEL_MAX
+};
+
 enum ovs_key_attr {
        OVS_KEY_ATTR_UNSPEC,
        OVS_KEY_ATTR_ENCAP,     /* Nested set of encapsulated attributes. */
@@ -282,8 +297,12 @@ 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_IPV4_TUNNEL,  /* struct ovs_key_ipv4_tunnel */
+       OVS_KEY_ATTR_TUNNEL,    /* Nested set of ovs_tunnel attributes */
        OVS_KEY_ATTR_TUN_ID = 63,  /* be64 tunnel ID */
+
+#ifdef __KERNEL__
+       OVS_KEY_ATTR_IPV4_TUNNEL,  /* struct ovs_key_ipv4_tunnel */
+#endif
        __OVS_KEY_ATTR_MAX
 };
 
@@ -365,21 +384,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/flow.c b/lib/flow.c
index a13519e..2cd6b2f 100644
--- a/lib/flow.c
+++ b/lib/flow.c
@@ -579,7 +579,6 @@ void
 flow_wildcards_init_exact(struct flow_wildcards *wc)
 {
     memset(&wc->masks, 0xff, sizeof wc->masks);
-    memset(wc->masks.zeros, 0, sizeof wc->masks.zeros);
 }
 
 /* Returns true if 'wc' matches every packet, false if 'wc' fixes any bits or
diff --git a/lib/flow.h b/lib/flow.h
index 8e79e62..323b248 100644
--- a/lib/flow.h
+++ b/lib/flow.h
@@ -63,9 +63,10 @@ struct flow_tnl {
     ovs_be64 tun_id;
     ovs_be32 ip_src;
     ovs_be32 ip_dst;
-    uint16_t flags;
+    uint32_t flags;
     uint8_t ip_tos;
     uint8_t ip_ttl;
+    uint8_t zeros[2];
 };
 
 /*
@@ -103,7 +104,6 @@ struct flow {
     uint8_t arp_tha[6];         /* ARP/ND target hardware address. */
     uint8_t nw_ttl;             /* IP TTL/Hop Limit. */
     uint8_t nw_frag;            /* FLOW_FRAG_* flags. */
-    uint8_t zeros[4];
 };
 BUILD_ASSERT_DECL(sizeof(struct flow) % 4 == 0);
 
diff --git a/lib/odp-util.c b/lib/odp-util.c
index e2f21da..4cb07fc 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 "ipv4_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);
@@ -687,6 +687,112 @@ odp_tun_flag_to_string(uint32_t flags)
     }
 }
 
+/* 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(uint32_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 uint32_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);
+}
+
+/* Returns OVS_TNL_* flags. */
+static int
+tun_key_from_attr(const struct nlattr *attr, struct flow_tnl *tun)
+{
+    static const struct nl_policy ipv4_tun_key_policy[] = {
+        [OVS_KEY_ATTR_TUN_ID] = { .type = NL_A_BE64, .optional = true },
+        [OVS_TUNNEL_IPV4_SRC] = { .type = NL_A_BE32, .optional = true },
+        [OVS_TUNNEL_IPV4_DST] = { .type = NL_A_BE32 },
+        [OVS_TUNNEL_TOS] = { .type = NL_A_U8, .optional = true },
+        [OVS_TUNNEL_TTL] = { .type = NL_A_U8, .optional = true },
+        [OVS_TUNNEL_FLAGS] = { .type = NL_A_U32, .optional = true },
+    };
+    struct nlattr *a[ARRAY_SIZE(ipv4_tun_key_policy)];
+
+    if (!nl_parse_nested(attr, ipv4_tun_key_policy, a, ARRAY_SIZE(a))) {
+        return -EINVAL;
+    }
+
+    tun->ip_dst = nl_attr_get_be32(a[OVS_TUNNEL_IPV4_DST]);
+
+    if (a[OVS_KEY_ATTR_TUN_ID]) {
+        tun->tun_id = nl_attr_get_be64(a[OVS_KEY_ATTR_TUN_ID]);
+    } else {
+        tun->tun_id = 0;
+    }
+    if (a[OVS_TUNNEL_IPV4_SRC]) {
+        tun->ip_src = nl_attr_get_be32(a[OVS_TUNNEL_IPV4_SRC]);
+    } else {
+        tun->ip_src = 0;
+    }
+
+    if (a[OVS_TUNNEL_TOS]) {
+        tun->ip_tos = nl_attr_get_u8(a[OVS_TUNNEL_TOS]);
+    } else {
+        tun->ip_tos = 0;
+    }
+    if (a[OVS_TUNNEL_TTL]) {
+        tun->ip_ttl = nl_attr_get_u8(a[OVS_TUNNEL_TTL]);
+    } else {
+        tun->ip_ttl = 0;
+    }
+    if (a[OVS_TUNNEL_FLAGS]) {
+        tun->flags = nl_attr_get_u32(a[OVS_TUNNEL_FLAGS]);
+    } else {
+        tun->flags = 0;
+    }
+    memset(tun->zeros, 0, sizeof tun->zeros);
+    return 0;
+}
+
+static void
+tun_key_to_attr(struct ofpbuf *a, const struct flow_tnl *tun_key, bool 
odp_flags)
+{
+    size_t tun_key_ofs;
+
+    tun_key_ofs = nl_msg_start_nested(a, OVS_KEY_ATTR_TUNNEL);
+
+    /* layouts differ, flags has different size */
+    if (tun_key->tun_id) {
+        nl_msg_put_be64(a, OVS_KEY_ATTR_TUN_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);
+    }
+
+    if (tun_key->flags) {
+        if (odp_flags) {
+            nl_msg_put_u32(a, OVS_TUNNEL_FLAGS, 
flow_to_odp_flags(tun_key->flags));
+        } else {
+            nl_msg_put_u32(a, OVS_TUNNEL_FLAGS, tun_key->flags);
+        }
+    }
+
+    nl_msg_end_nested(a, tun_key_ofs);
+}
+
 static void
 format_odp_key_attr(const struct nlattr *a, struct ds *ds)
 {
@@ -699,7 +805,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,17 +840,17 @@ 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);
+    case OVS_KEY_ATTR_TUNNEL:
+        tun_key_from_attr(a, &tun_key);
         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);
+                      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, odp_tun_flag_to_string,
-                     ipv4_tun_key->tun_flags, ',');
+                     tun_key.flags, ',');
         ds_put_format(ds, "))");
         break;
 
@@ -974,23 +1080,23 @@ 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],"
                    "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;
 
             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], odp_tun_flag_to_string,
-                              &tun_key.tun_flags);
+                              &tun_key.flags);
             if (res < 0) {
                 return res;
             }
@@ -1000,9 +1106,7 @@ parse_odp_key_attr(const char *s, const struct simap 
*port_names,
             }
             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);
+            tun_key_to_attr(key, &tun_key, false);
             return n;
         }
     }
@@ -1342,27 +1446,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 +1466,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, true);
     } else if (flow->tunnel.tun_id != htonll(0)) {
         nl_msg_put_be64(buf, OVS_KEY_ATTR_TUN_ID, flow->tunnel.tun_id);
     }
@@ -1905,23 +1977,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;
+    if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_TUNNEL)) {
 
-        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 (tun_key_from_attr(attrs[OVS_KEY_ATTR_TUNNEL], &flow->tunnel))
+            return ODP_FIT_ERROR;
 
+        flow->tunnel.flags = odp_to_flow_flags(flow->tunnel.flags);
         /* 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;
+        if (!(flow->tunnel.flags & ~OVS_TNL_F_KNOWN_MASK)) {
+            expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_TUNNEL;
         }
     }
 
@@ -2017,18 +2083,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, true);
+        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/test-bundle.c b/tests/test-bundle.c
index aa8b6f0..f2d9b82 100644
--- a/tests/test-bundle.c
+++ b/tests/test-bundle.c
@@ -136,7 +136,6 @@ main(int argc, char *argv[])
     flows = xmalloc(N_FLOWS * sizeof *flows);
     for (i = 0; i < N_FLOWS; i++) {
         random_bytes(&flows[i], sizeof flows[i]);
-        memset(flows[i].zeros, 0, sizeof flows[i].zeros);
         flows[i].regs[0] = OFPP_NONE;
     }
 
diff --git a/tests/test-multipath.c b/tests/test-multipath.c
index b990c13..8a35567 100644
--- a/tests/test-multipath.c
+++ b/tests/test-multipath.c
@@ -60,7 +60,6 @@ main(int argc, char *argv[])
             struct flow flow;
 
             random_bytes(&flow, sizeof flow);
-            memset(flow.zeros, 0, sizeof flow.zeros);
 
             mp.max_link = n - 1;
             multipath_execute(&mp, &flow);
-- 
1.7.10

_______________________________________________
dev mailing list
dev@openvswitch.org
http://openvswitch.org/mailman/listinfo/dev

Reply via email to