Extend sFlow agent to report tunnel and MPLS structures
[cascardo/ovs.git] / lib / odp-util.c
index f99683a..eec0bfb 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009, 2010, 2011, 2012, 2013, 2014 Nicira, Inc.
+ * Copyright (c) 2009, 2010, 2011, 2012, 2013, 2014, 2015 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -35,6 +35,7 @@
 #include "packets.h"
 #include "simap.h"
 #include "timeval.h"
+#include "tun-metadata.h"
 #include "unaligned.h"
 #include "util.h"
 #include "uuid.h"
@@ -68,6 +69,17 @@ static void format_odp_key_attr(const struct nlattr *a,
                                 const struct hmap *portno_names, struct ds *ds,
                                 bool verbose);
 
+struct geneve_scan {
+    struct geneve_opt d[63];
+    int len;
+};
+
+static int scan_geneve(const char *s, struct geneve_scan *key,
+                       struct geneve_scan *mask);
+static void format_geneve_opts(const struct geneve_opt *opt,
+                               const struct geneve_opt *mask, int opts_len,
+                               struct ds *, bool verbose);
+
 static struct nlattr *generate_all_wildcard_mask(const struct attr_len_tbl tbl[],
                                                  int max, struct ofpbuf *,
                                                  const struct nlattr *key);
@@ -223,130 +235,11 @@ slow_path_reason_to_explanation(enum slow_path_reason reason)
 }
 
 static int
-parse_flags(const char *s, const char *(*bit_to_string)(uint32_t),
-            uint32_t *res_flags, uint32_t allowed, uint32_t *res_mask)
+parse_odp_flags(const char *s, const char *(*bit_to_string)(uint32_t),
+                uint32_t *res_flags, uint32_t allowed, uint32_t *res_mask)
 {
-    uint32_t result = 0;
-    int n;
-
-    /* Parse masked flags in numeric format? */
-    if (res_mask && ovs_scan(s, "%"SCNi32"/%"SCNi32"%n",
-                             res_flags, res_mask, &n) && n > 0) {
-        if (*res_flags & ~allowed || *res_mask & ~allowed) {
-            return -EINVAL;
-        }
-        return n;
-    }
-
-    n = 0;
-
-    if (res_mask && (*s == '+' || *s == '-')) {
-        uint32_t flags = 0, mask = 0;
-
-        /* Parse masked flags. */
-        while (s[0] != ')') {
-            bool set;
-            uint32_t bit;
-            int name_len;
-
-            if (s[0] == '+') {
-                set = true;
-            } else if (s[0] == '-') {
-                set = false;
-            } else {
-                return -EINVAL;
-            }
-            s++;
-            n++;
-
-            name_len = strcspn(s, "+-)");
-
-            for (bit = 1; bit; bit <<= 1) {
-                const char *fname = bit_to_string(bit);
-                size_t len;
-
-                if (!fname) {
-                    continue;
-                }
-
-                len = strlen(fname);
-                if (len != name_len) {
-                    continue;
-                }
-                if (!strncmp(s, fname, len)) {
-                    if (mask & bit) {
-                        /* bit already set. */
-                        return -EINVAL;
-                    }
-                    if (!(bit & allowed)) {
-                        return -EINVAL;
-                    }
-                    if (set) {
-                        flags |= bit;
-                    }
-                    mask |= bit;
-                    break;
-                }
-            }
-
-            if (!bit) {
-                return -EINVAL; /* Unknown flag name */
-            }
-            s += name_len;
-            n += name_len;
-        }
-
-        *res_flags = flags;
-        *res_mask = mask;
-        return n;
-    }
-
-    /* Parse unmasked flags.  If a flag is present, it is set, otherwise
-     * it is not set. */
-    while (s[n] != ')') {
-        unsigned long long int flags;
-        uint32_t bit;
-        int n0;
-
-        if (ovs_scan(&s[n], "%lli%n", &flags, &n0)) {
-            if (flags & ~allowed) {
-                return -EINVAL;
-            }
-            n += n0 + (s[n + n0] == ',');
-            result |= flags;
-            continue;
-        }
-
-        for (bit = 1; bit; bit <<= 1) {
-            const char *name = bit_to_string(bit);
-            size_t len;
-
-            if (!name) {
-                continue;
-            }
-
-            len = strlen(name);
-            if (!strncmp(s + n, name, len) &&
-                (s[n + len] == ',' || s[n + len] == ')')) {
-                if (!(bit & allowed)) {
-                    return -EINVAL;
-                }
-                result |= bit;
-                n += len + (s[n + len] == ',');
-                break;
-            }
-        }
-
-        if (!bit) {
-            return -EINVAL;
-        }
-    }
-
-    *res_flags = result;
-    if (res_mask) {
-        *res_mask = UINT32_MAX;
-    }
-    return n;
+    return parse_flags(s, bit_to_string, ')', NULL, NULL,
+                       res_flags, allowed, res_mask);
 }
 
 static void
@@ -358,6 +251,8 @@ format_odp_userspace_action(struct ds *ds, const struct nlattr *attr)
                                           .optional = true },
         [OVS_USERSPACE_ATTR_EGRESS_TUN_PORT] = { .type = NL_A_U32,
                                                  .optional = true },
+        [OVS_USERSPACE_ATTR_ACTIONS] = { .type = NL_A_UNSPEC,
+                                                 .optional = true },
     };
     struct nlattr *a[ARRAY_SIZE(ovs_userspace_policy)];
     const struct nlattr *userdata_attr;
@@ -429,6 +324,10 @@ format_odp_userspace_action(struct ds *ds, const struct nlattr *attr)
         }
     }
 
+    if (a[OVS_USERSPACE_ATTR_ACTIONS]) {
+        ds_put_cstr(ds, ",actions");
+    }
+
     tunnel_out_port_attr = a[OVS_USERSPACE_ATTR_EGRESS_TUN_PORT];
     if (tunnel_out_port_attr) {
         ds_put_format(ds, ",tunnel_out_port=%"PRIu32,
@@ -580,9 +479,19 @@ format_odp_tnl_push_header(struct ds *ds, struct ovs_action_push_tnl *data)
 
         gnh = format_udp_tnl_push_header(ds, ip);
 
-        ds_put_format(ds, "geneve(%svni=0x%"PRIx32")",
+        ds_put_format(ds, "geneve(%s%svni=0x%"PRIx32,
                       gnh->oam ? "oam," : "",
+                      gnh->critical ? "crit," : "",
                       ntohl(get_16aligned_be32(&gnh->vni)) >> 8);
+        if (gnh->opt_len) {
+            ds_put_cstr(ds, ",options(");
+            format_geneve_opts(gnh->options, NULL, gnh->opt_len * 4,
+                               ds, false);
+            ds_put_char(ds, ')');
+        }
+
+        ds_put_char(ds, ')');
     } else if (data->tnl_type == OVS_VPORT_TYPE_GRE) {
         const struct gre_base_hdr *greh;
         ovs_16aligned_be32 *options;
@@ -763,6 +672,7 @@ parse_odp_userspace_action(const char *s, struct ofpbuf *actions)
     int n = -1;
     void *user_data = NULL;
     size_t user_data_size = 0;
+    bool include_actions = false;
 
     if (!ovs_scan(s, "userspace(pid=%"SCNi32"%n", &pid, &n)) {
         return -EINVAL;
@@ -801,9 +711,9 @@ parse_odp_userspace_action(const char *s, struct ofpbuf *actions)
             cookie.slow_path.unused = 0;
             cookie.slow_path.reason = 0;
 
-            res = parse_flags(&s[n], slow_path_reason_to_string,
-                              &cookie.slow_path.reason,
-                              SLOW_PATH_REASON_MASK, NULL);
+            res = parse_odp_flags(&s[n], slow_path_reason_to_string,
+                                  &cookie.slow_path.reason,
+                                  SLOW_PATH_REASON_MASK, NULL);
             if (res < 0 || s[n + res] != ')') {
                 return res;
             }
@@ -849,14 +759,24 @@ parse_odp_userspace_action(const char *s, struct ofpbuf *actions)
         }
     }
 
+    {
+        int n1 = -1;
+        if (ovs_scan(&s[n], ",actions%n", &n1)) {
+            n += n1;
+            include_actions = true;
+        }
+    }
+
     {
         int n1 = -1;
         if (ovs_scan(&s[n], ",tunnel_out_port=%"SCNi32")%n",
                      &tunnel_out_port, &n1)) {
-            odp_put_userspace_action(pid, user_data, user_data_size, tunnel_out_port, actions);
+            odp_put_userspace_action(pid, user_data, user_data_size,
+                                     tunnel_out_port, include_actions, actions);
             return n + n1;
         } else if (s[n] == ')') {
-            odp_put_userspace_action(pid, user_data, user_data_size, ODPP_NONE, actions);
+            odp_put_userspace_action(pid, user_data, user_data_size,
+                                     ODPP_NONE, include_actions, actions);
             return n + 1;
         }
     }
@@ -938,17 +858,41 @@ ovs_parse_tnl_push(const char *s, struct ovs_action_push_tnl *data)
             struct genevehdr *gnh = (struct genevehdr *) (udp + 1);
 
             memset(gnh, 0, sizeof *gnh);
+            header_len = sizeof *eth + sizeof *ip +
+                         sizeof *udp + sizeof *gnh;
+
             if (ovs_scan_len(s, &n, "oam,")) {
                 gnh->oam = 1;
             }
-            if (!ovs_scan_len(s, &n, "vni=0x%"SCNx32"))", &vni)) {
+            if (ovs_scan_len(s, &n, "crit,")) {
+                gnh->critical = 1;
+            }
+            if (!ovs_scan_len(s, &n, "vni=%"SCNi32, &vni)) {
+                return -EINVAL;
+            }
+            if (ovs_scan_len(s, &n, ",options(")) {
+                struct geneve_scan options;
+                int len;
+
+                memset(&options, 0, sizeof options);
+                len = scan_geneve(s + n, &options, NULL);
+                if (!len) {
+                    return -EINVAL;
+                }
+
+                memcpy(gnh->options, options.d, options.len);
+                gnh->opt_len = options.len / 4;
+                header_len += options.len;
+
+                n += len;
+            }
+            if (!ovs_scan_len(s, &n, "))")) {
                 return -EINVAL;
             }
+
             gnh->proto_type = htons(ETH_TYPE_TEB);
             put_16aligned_be32(&gnh->vni, htonl(vni << 8));
             tnl_type = OVS_VPORT_TYPE_GENEVE;
-            header_len = sizeof *eth + sizeof *ip +
-                         sizeof *udp + sizeof *gnh;
         } else {
             return -EINVAL;
         }
@@ -1317,41 +1261,10 @@ ovs_frag_type_to_string(enum ovs_frag_type type)
     }
 }
 
-#define GENEVE_OPT(class, type) ((OVS_FORCE uint32_t)(class) << 8 | (type))
-static int
-parse_geneve_opts(const struct nlattr *attr)
-{
-    int opts_len = nl_attr_get_size(attr);
-    const struct geneve_opt *opt = nl_attr_get(attr);
-
-    while (opts_len > 0) {
-        int len;
-
-        if (opts_len < sizeof(*opt)) {
-            return -EINVAL;
-        }
-
-        len = sizeof(*opt) + opt->length * 4;
-        if (len > opts_len) {
-            return -EINVAL;
-        }
-
-        switch (GENEVE_OPT(opt->opt_class, opt->type)) {
-        default:
-            if (opt->type & GENEVE_CRIT_OPT_TYPE) {
-                return -EINVAL;
-            }
-        };
-
-        opt = opt + len / sizeof(*opt);
-        opts_len -= len;
-    };
-
-    return 0;
-}
-
-enum odp_key_fitness
-odp_tun_key_from_attr(const struct nlattr *attr, struct flow_tnl *tun)
+static enum odp_key_fitness
+odp_tun_key_from_attr__(const struct nlattr *attr,
+                        const struct nlattr *flow_attrs, size_t flow_attr_len,
+                        const struct flow_tnl *src_tun, struct flow_tnl *tun)
 {
     unsigned int left;
     const struct nlattr *a;
@@ -1420,15 +1333,14 @@ odp_tun_key_from_attr(const struct nlattr *attr, struct flow_tnl *tun)
 
             break;
         }
-        case OVS_TUNNEL_KEY_ATTR_GENEVE_OPTS: {
-            if (parse_geneve_opts(a)) {
+        case OVS_TUNNEL_KEY_ATTR_GENEVE_OPTS:
+            if (tun_metadata_from_geneve_nlattr(a, flow_attrs, flow_attr_len,
+                                                &src_tun->metadata,
+                                                &tun->metadata)) {
                 return ODP_FIT_ERROR;
             }
-            /* It is necessary to reproduce options exactly (including order)
-             * so it's easiest to just echo them back. */
-            unknown = true;
             break;
-        }
+
         default:
             /* Allow this to show up as unexpected, if there are unknown
              * tunnel attribute, eventually resulting in ODP_FIT_TOO_MUCH. */
@@ -1446,8 +1358,17 @@ odp_tun_key_from_attr(const struct nlattr *attr, struct flow_tnl *tun)
     return ODP_FIT_PERFECT;
 }
 
+enum odp_key_fitness
+odp_tun_key_from_attr(const struct nlattr *attr, struct flow_tnl *tun)
+{
+    memset(tun, 0, sizeof *tun);
+    return odp_tun_key_from_attr__(attr, NULL, 0, NULL, tun);
+}
+
 static void
-tun_key_to_attr(struct ofpbuf *a, const struct flow_tnl *tun_key)
+tun_key_to_attr(struct ofpbuf *a, const struct flow_tnl *tun_key,
+                const struct flow_tnl *tun_flow_key,
+                const struct ofpbuf *key_buf)
 {
     size_t tun_key_ofs;
 
@@ -1491,6 +1412,13 @@ tun_key_to_attr(struct ofpbuf *a, const struct flow_tnl *tun_key)
         nl_msg_end_nested(a, vxlan_opts_ofs);
     }
 
+    if (tun_key == tun_flow_key) {
+        tun_metadata_to_geneve_nlattr_flow(&tun_key->metadata, a);
+    } else {
+        tun_metadata_to_geneve_nlattr_mask(key_buf, &tun_key->metadata,
+                                           &tun_flow_key->metadata, a);
+    }
+
     nl_msg_end_nested(a, tun_key_ofs);
 }
 
@@ -1520,18 +1448,7 @@ odp_mask_is_exact(enum ovs_key_attr attr, const void *mask, size_t size)
             && ipv6_mask_is_exact((const struct in6_addr *)ipv6_mask->ipv6_dst);
     }
     if (attr == OVS_KEY_ATTR_TUNNEL) {
-        const struct flow_tnl *tun_mask = mask;
-
-        return tun_mask->flags == FLOW_TNL_F_MASK
-            && tun_mask->tun_id == OVS_BE64_MAX
-            && tun_mask->ip_src == OVS_BE32_MAX
-            && tun_mask->ip_dst == OVS_BE32_MAX
-            && tun_mask->ip_tos == UINT8_MAX
-            && tun_mask->ip_ttl == UINT8_MAX
-            && tun_mask->tp_src == OVS_BE16_MAX
-            && tun_mask->tp_dst == OVS_BE16_MAX
-            && tun_mask->gbp_id == OVS_BE16_MAX
-            && tun_mask->gbp_flags == UINT8_MAX;
+        return false;
     }
 
     if (attr == OVS_KEY_ATTR_ARP) {
@@ -1548,16 +1465,12 @@ odp_mask_is_exact(enum ovs_key_attr attr, const void *mask, size_t size)
 static bool
 odp_mask_attr_is_exact(const struct nlattr *ma)
 {
-    struct flow_tnl tun_mask;
     enum ovs_key_attr attr = nl_attr_type(ma);
     const void *mask;
     size_t size;
 
     if (attr == OVS_KEY_ATTR_TUNNEL) {
-        memset(&tun_mask, 0, sizeof tun_mask);
-        odp_tun_key_from_attr(ma, &tun_mask);
-        mask = &tun_mask;
-        size = sizeof tun_mask;
+        return false;
     } else {
         mask = nl_attr_get(ma);
         size = nl_attr_get_size(ma);
@@ -1776,14 +1689,13 @@ format_tun_flags(struct ds *ds, const char *name, uint16_t key,
     bool mask_empty = mask && !*mask;
 
     if (verbose || !mask_empty) {
-        bool mask_full = !mask || (*mask & FLOW_TNL_F_MASK) == FLOW_TNL_F_MASK;
-
         ds_put_cstr(ds, name);
         ds_put_char(ds, '(');
-        if (!mask_full) { /* Partially masked. */
-            format_flags_masked(ds, NULL, flow_tun_flag_to_string, key, *mask);
+        if (mask) {
+            format_flags_masked(ds, NULL, flow_tun_flag_to_string, key,
+                                *mask & FLOW_TNL_F_MASK, FLOW_TNL_F_MASK);
         } else { /* Fully masked. */
-            format_flags(ds, flow_tun_flag_to_string, key, ',');
+            format_flags(ds, flow_tun_flag_to_string, key, '|');
         }
         ds_put_cstr(ds, "),");
     }
@@ -1904,21 +1816,10 @@ format_odp_tun_vxlan_opt(const struct nlattr *attr,
 #define MASK(PTR, FIELD) PTR ? &PTR->FIELD : NULL
 
 static void
-format_odp_tun_geneve(const struct nlattr *attr,
-                      const struct nlattr *mask_attr, struct ds *ds,
-                      bool verbose)
+format_geneve_opts(const struct geneve_opt *opt,
+                   const struct geneve_opt *mask, int opts_len,
+                   struct ds *ds, bool verbose)
 {
-    int opts_len = nl_attr_get_size(attr);
-    const struct geneve_opt *opt = nl_attr_get(attr);
-    const struct geneve_opt *mask = mask_attr ?
-                                    nl_attr_get(mask_attr) : NULL;
-
-    if (mask && nl_attr_get_size(attr) != nl_attr_get_size(mask_attr)) {
-        ds_put_format(ds, "value len %"PRIuSIZE" different from mask len %"PRIuSIZE,
-                      nl_attr_get_size(attr), nl_attr_get_size(mask_attr));
-        return;
-    }
-
     while (opts_len > 0) {
         unsigned int len;
         uint8_t data_len, data_len_mask;
@@ -1968,6 +1869,25 @@ format_odp_tun_geneve(const struct nlattr *attr,
     };
 }
 
+static void
+format_odp_tun_geneve(const struct nlattr *attr,
+                      const struct nlattr *mask_attr, struct ds *ds,
+                      bool verbose)
+{
+    int opts_len = nl_attr_get_size(attr);
+    const struct geneve_opt *opt = nl_attr_get(attr);
+    const struct geneve_opt *mask = mask_attr ?
+                                    nl_attr_get(mask_attr) : NULL;
+
+    if (mask && nl_attr_get_size(attr) != nl_attr_get_size(mask_attr)) {
+        ds_put_format(ds, "value len %"PRIuSIZE" different from mask len %"PRIuSIZE,
+                      nl_attr_get_size(attr), nl_attr_get_size(mask_attr));
+        return;
+    }
+
+    format_geneve_opts(opt, mask, opts_len, ds, verbose);
+}
+
 static void
 format_odp_tun_attr(const struct nlattr *attr, const struct nlattr *mask_attr,
                     struct ds *ds, bool verbose)
@@ -2254,10 +2174,11 @@ format_odp_key_attr(const struct nlattr *a, const struct nlattr *ma,
         if (!is_exact) {
             format_flags_masked(ds, NULL, packet_tcp_flag_to_string,
                                 ntohs(nl_attr_get_be16(a)),
-                                ntohs(nl_attr_get_be16(ma)));
+                                TCP_FLAGS(nl_attr_get_be16(ma)),
+                                TCP_FLAGS(OVS_BE16_MAX));
         } else {
             format_flags(ds, packet_tcp_flag_to_string,
-                         ntohs(nl_attr_get_be16(a)), ',');
+                         ntohs(nl_attr_get_be16(a)), '|');
         }
         break;
 
@@ -2657,8 +2578,8 @@ scan_tun_flags(const char *s, uint16_t *key, uint16_t *mask)
     uint32_t flags, fmask;
     int n;
 
-    n = parse_flags(s, flow_tun_flag_to_string, &flags,
-                    FLOW_TNL_F_MASK, mask ? &fmask : NULL);
+    n = parse_odp_flags(s, flow_tun_flag_to_string, &flags,
+                        FLOW_TNL_F_MASK, mask ? &fmask : NULL);
     if (n >= 0 && s[n] == ')') {
         *key = flags;
         if (mask) {
@@ -2675,8 +2596,8 @@ scan_tcp_flags(const char *s, ovs_be16 *key, ovs_be16 *mask)
     uint32_t flags, fmask;
     int n;
 
-    n = parse_flags(s, packet_tcp_flag_to_string, &flags,
-                    TCP_FLAGS(OVS_BE16_MAX), mask ? &fmask : NULL);
+    n = parse_odp_flags(s, packet_tcp_flag_to_string, &flags,
+                        TCP_FLAGS(OVS_BE16_MAX), mask ? &fmask : NULL);
     if (n >= 0) {
         *key = htons(flags);
         if (mask) {
@@ -2906,11 +2827,6 @@ scan_vxlan_gbp(const char *s, uint32_t *key, uint32_t *mask)
     return 0;
 }
 
-struct geneve_scan {
-    struct geneve_opt d[63];
-    int len;
-};
-
 static int
 scan_geneve(const char *s, struct geneve_scan *key, struct geneve_scan *mask)
 {
@@ -3432,31 +3348,32 @@ static void get_tp_key(const struct flow *, union ovs_key_tp *);
 static void put_tp_key(const union ovs_key_tp *, struct flow *);
 
 static void
-odp_flow_key_from_flow__(struct ofpbuf *buf, const struct flow *flow,
-                         const struct flow *mask, odp_port_t odp_in_port,
-                         size_t max_mpls_depth, bool recirc, bool export_mask)
+odp_flow_key_from_flow__(const struct odp_flow_key_parms *parms,
+                         bool export_mask, struct ofpbuf *buf)
 {
     struct ovs_key_ethernet *eth_key;
     size_t encap;
-    const struct flow *data = export_mask ? mask : flow;
+    const struct flow *flow = parms->flow;
+    const struct flow *data = export_mask ? parms->mask : parms->flow;
 
     nl_msg_put_u32(buf, OVS_KEY_ATTR_PRIORITY, data->skb_priority);
 
     if (flow->tunnel.ip_dst || export_mask) {
-        tun_key_to_attr(buf, &data->tunnel);
+        tun_key_to_attr(buf, &data->tunnel, &parms->flow->tunnel,
+                        parms->key_buf);
     }
 
     nl_msg_put_u32(buf, OVS_KEY_ATTR_SKB_MARK, data->pkt_mark);
 
-    if (recirc) {
+    if (parms->support.recirc) {
         nl_msg_put_u32(buf, OVS_KEY_ATTR_RECIRC_ID, data->recirc_id);
         nl_msg_put_u32(buf, OVS_KEY_ATTR_DP_HASH, data->dp_hash);
     }
 
     /* Add an ingress port attribute if this is a mask or 'odp_in_port'
      * is not the magical value "ODPP_NONE". */
-    if (export_mask || odp_in_port != ODPP_NONE) {
-        nl_msg_put_odp_port(buf, OVS_KEY_ATTR_IN_PORT, odp_in_port);
+    if (export_mask || parms->odp_in_port != ODPP_NONE) {
+        nl_msg_put_odp_port(buf, OVS_KEY_ATTR_IN_PORT, parms->odp_in_port);
     }
 
     eth_key = nl_msg_put_unspec_uninit(buf, OVS_KEY_ATTR_ETHERNET,
@@ -3522,7 +3439,9 @@ odp_flow_key_from_flow__(struct ofpbuf *buf, const struct flow *flow,
         int i, n;
 
         n = flow_count_mpls_labels(flow, NULL);
-        n = MIN(n, max_mpls_depth);
+        if (export_mask) {
+            n = MIN(n, parms->support.max_mpls_depth);
+        }
         mpls_key = nl_msg_put_unspec_uninit(buf, OVS_KEY_ATTR_MPLS,
                                             n * sizeof *mpls_key);
         for (i = 0; i < n; i++) {
@@ -3594,43 +3513,26 @@ unencap:
 }
 
 /* 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'
- * is anything other than ODPP_NONE, it is included in 'buf' as the input
- * port.
  *
  * 'buf' must have at least ODPUTIL_FLOW_KEY_BYTES bytes of space, or be
- * capable of being expanded to allow for that much space.
- *
- * 'recirc' indicates support for recirculation fields. If this is true, then
- * these fields will always be serialised. */
+ * capable of being expanded to allow for that much space. */
 void
-odp_flow_key_from_flow(struct ofpbuf *buf, const struct flow *flow,
-                       const struct flow *mask, odp_port_t odp_in_port,
-                       bool recirc)
+odp_flow_key_from_flow(const struct odp_flow_key_parms *parms,
+                       struct ofpbuf *buf)
 {
-    odp_flow_key_from_flow__(buf, flow, mask, odp_in_port, SIZE_MAX, recirc,
-                             false);
+    odp_flow_key_from_flow__(parms, false, buf);
 }
 
 /* Appends a representation of 'mask' as OVS_KEY_ATTR_* attributes to
- * 'buf'.  'flow' is used as a template to determine how to interpret
- * 'mask'.  For example, the 'dl_type' of 'mask' describes the mask, but
- * it doesn't indicate whether the other fields should be interpreted as
- * ARP, IPv4, IPv6, etc.
+ * 'buf'.
  *
  * 'buf' must have at least ODPUTIL_FLOW_KEY_BYTES bytes of space, or be
- * capable of being expanded to allow for that much space.
- *
- * 'recirc' indicates support for recirculation fields. If this is true, then
- * these fields will always be serialised. */
+ * capable of being expanded to allow for that much space. */
 void
-odp_flow_key_from_mask(struct ofpbuf *buf, const struct flow *mask,
-                       const struct flow *flow, uint32_t odp_in_port_mask,
-                       size_t max_mpls_depth, bool recirc)
+odp_flow_key_from_mask(const struct odp_flow_key_parms *parms,
+                       struct ofpbuf *buf)
 {
-    odp_flow_key_from_flow__(buf, flow, mask, u32_to_odp(odp_in_port_mask),
-                             max_mpls_depth, recirc, true);
+    odp_flow_key_from_flow__(parms, true, buf);
 }
 
 /* Generate ODP flow key from the given packet metadata */
@@ -3640,7 +3542,7 @@ odp_key_from_pkt_metadata(struct ofpbuf *buf, const struct pkt_metadata *md)
     nl_msg_put_u32(buf, OVS_KEY_ATTR_PRIORITY, md->skb_priority);
 
     if (md->tunnel.ip_dst) {
-        tun_key_to_attr(buf, &md->tunnel);
+        tun_key_to_attr(buf, &md->tunnel, &md->tunnel, NULL);
     }
 
     nl_msg_put_u32(buf, OVS_KEY_ATTR_SKB_MARK, md->pkt_mark);
@@ -3663,7 +3565,7 @@ odp_key_to_pkt_metadata(const struct nlattr *key, size_t key_len,
         1u << OVS_KEY_ATTR_SKB_MARK | 1u << OVS_KEY_ATTR_TUNNEL |
         1u << OVS_KEY_ATTR_IN_PORT;
 
-    *md = PKT_METADATA_INITIALIZER(ODPP_NONE);
+    pkt_metadata_init(md, ODPP_NONE);
 
     NL_ATTR_FOR_EACH (nla, left, key, key_len) {
         uint16_t type = nl_attr_type(nla);
@@ -4204,6 +4106,7 @@ parse_8021q_onward(const struct nlattr *attrs[OVS_KEY_ATTR_MAX + 1],
 
 static enum odp_key_fitness
 odp_flow_key_to_flow__(const struct nlattr *key, size_t key_len,
+                       const struct nlattr *src_key, size_t src_key_len,
                        struct flow *flow, const struct flow *src_flow)
 {
     const struct nlattr *attrs[OVS_KEY_ATTR_MAX + 1];
@@ -4247,7 +4150,9 @@ odp_flow_key_to_flow__(const struct nlattr *key, size_t key_len,
     if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_TUNNEL)) {
         enum odp_key_fitness res;
 
-        res = odp_tun_key_from_attr(attrs[OVS_KEY_ATTR_TUNNEL], &flow->tunnel);
+        res = odp_tun_key_from_attr__(attrs[OVS_KEY_ATTR_TUNNEL], src_key,
+                                      src_key_len, &src_flow->tunnel,
+                                      &flow->tunnel);
         if (res == ODP_FIT_ERROR) {
             return ODP_FIT_ERROR;
         } else if (res == ODP_FIT_PERFECT) {
@@ -4319,18 +4224,21 @@ enum odp_key_fitness
 odp_flow_key_to_flow(const struct nlattr *key, size_t key_len,
                      struct flow *flow)
 {
-   return odp_flow_key_to_flow__(key, key_len, flow, flow);
+   return odp_flow_key_to_flow__(key, key_len, NULL, 0, flow, flow);
 }
 
-/* Converts the 'key_len' bytes of OVS_KEY_ATTR_* attributes in 'key' to a mask
- * structure in 'mask'.  'flow' must be a previously translated flow
- * corresponding to 'mask'.  Returns an ODP_FIT_* value that indicates how well
- * 'key' fits our expectations for what a flow key should contain. */
+/* Converts the 'mask_key_len' bytes of OVS_KEY_ATTR_* attributes in 'mask_key'
+ * to a mask structure in 'mask'.  'flow' must be a previously translated flow
+ * corresponding to 'mask' and similarly flow_key/flow_key_len must be the
+ * attributes from that flow.  Returns an ODP_FIT_* value that indicates how
+ * well 'key' fits our expectations for what a flow key should contain. */
 enum odp_key_fitness
-odp_flow_key_to_mask(const struct nlattr *key, size_t key_len,
+odp_flow_key_to_mask(const struct nlattr *mask_key, size_t mask_key_len,
+                     const struct nlattr *flow_key, size_t flow_key_len,
                      struct flow *mask, const struct flow *flow)
 {
-   return odp_flow_key_to_flow__(key, key_len, mask, flow);
+   return odp_flow_key_to_flow__(mask_key, mask_key_len, flow_key, flow_key_len,
+                                 mask, flow);
 }
 
 /* Returns 'fitness' as a string, for use in debug messages. */
@@ -4360,6 +4268,7 @@ size_t
 odp_put_userspace_action(uint32_t pid,
                          const void *userdata, size_t userdata_size,
                          odp_port_t tunnel_out_port,
+                         bool include_actions,
                          struct ofpbuf *odp_actions)
 {
     size_t userdata_ofs;
@@ -4390,6 +4299,9 @@ odp_put_userspace_action(uint32_t pid,
         nl_msg_put_odp_port(odp_actions, OVS_USERSPACE_ATTR_EGRESS_TUN_PORT,
                             tunnel_out_port);
     }
+    if (include_actions) {
+        nl_msg_put_flag(odp_actions, OVS_USERSPACE_ATTR_ACTIONS);
+    }
     nl_msg_end_nested(odp_actions, offset);
 
     return userdata_ofs;
@@ -4400,7 +4312,7 @@ odp_put_tunnel_action(const struct flow_tnl *tunnel,
                       struct ofpbuf *odp_actions)
 {
     size_t offset = nl_msg_start_nested(odp_actions, OVS_ACTION_ATTR_SET);
-    tun_key_to_attr(odp_actions, tunnel);
+    tun_key_to_attr(odp_actions, tunnel, tunnel, NULL);
     nl_msg_end_nested(odp_actions, offset);
 }