[ovs-dev] [megaflow v3 2/2] ovs-dpctl: Add mega flow support

Ethan Jackson ethan at nicira.com
Wed Jun 12 00:39:04 UTC 2013


This doesn't constitute a review but two things

s/cerata/create

Do we really need the --mega option?  Seems like that would be a
reasonable default given that the most important implementations will
be running megaflows.

Ethan


On Tue, Jun 11, 2013 at 4:45 PM, Andy Zhou <azhou at nicira.com> wrote:
> Added --mega option to ovs-dpctl command to allow mega flow to be
> specified and displayed.  ovs-dpctl tool is mainly used as debugging
> tool.
>
> This patch also implements the low level user space routines to send
> and receive mega flow netlink messages. Those netlink support
> routines are required for forthcoming user space mega flow patches.
>
> Ethan contributed current version of ovs-dpctl mega flow output
> function.
>
> Co-authored-by: Ethan Jackson <ethan at nicira.com>
> Signed-off-by: Ethan Jackson <ethan at nicira.com>
> Signed-off-by: Andy Zhou <azhou at nicira.com>
>
> ---
> v1->v2
>      Integrated Ethan's patch on ovs-dpctl mega flow output.
>      Add Ethan as a co-author for this patch.
>
> v2->v3
>      Rebase to head to make review easier.
>      ovs-dpctl: Add mask input for tunnel configurations.
> ---
>  lib/dpif-linux.c         |   19 +
>  lib/dpif-netdev.c        |    6 +
>  lib/dpif-provider.h      |    1 +
>  lib/dpif.c               |    9 +
>  lib/dpif.h               |    4 +
>  lib/netdev-dummy.c       |    2 +-
>  lib/odp-util.c           | 1218 +++++++++++++++++++++++++++++++++++++++++-----
>  lib/odp-util.h           |   11 +-
>  ofproto/ofproto-dpif.c   |   17 +-
>  tests/test-odp.c         |    4 +-
>  utilities/ovs-dpctl.8.in |    8 +
>  utilities/ovs-dpctl.c    |   44 +-
>  12 files changed, 1194 insertions(+), 149 deletions(-)
>
> diff --git a/lib/dpif-linux.c b/lib/dpif-linux.c
> index 1383b58..a084c3b 100644
> --- a/lib/dpif-linux.c
> +++ b/lib/dpif-linux.c
> @@ -105,6 +105,8 @@ struct dpif_linux_flow {
>       * the Netlink version of the command, even if actions_len is zero. */
>      const struct nlattr *key;           /* OVS_FLOW_ATTR_KEY. */
>      size_t key_len;
> +    const struct nlattr *mask;           /* OVS_FLOW_ATTR_KEY. */
> +    size_t mask_len;
>      const struct nlattr *actions;       /* OVS_FLOW_ATTR_ACTIONS. */
>      size_t actions_len;
>      const struct ovs_flow_stats *stats; /* OVS_FLOW_ATTR_STATS. */
> @@ -807,6 +809,8 @@ dpif_linux_init_flow_put(struct dpif *dpif_, const struct dpif_flow_put *put,
>      request->dp_ifindex = dpif->dp_ifindex;
>      request->key = put->key;
>      request->key_len = put->key_len;
> +    request->mask = put->mask;
> +    request->mask_len = put->mask_len;
>      /* Ensure that OVS_FLOW_ATTR_ACTIONS will always be included. */
>      request->actions = (put->actions
>                          ? put->actions
> @@ -901,6 +905,7 @@ dpif_linux_flow_dump_start(const struct dpif *dpif_, void **statep)
>  static int
>  dpif_linux_flow_dump_next(const struct dpif *dpif_ OVS_UNUSED, void *state_,
>                            const struct nlattr **key, size_t *key_len,
> +                          const struct nlattr **mask, size_t *mask_len,
>                            const struct nlattr **actions, size_t *actions_len,
>                            const struct dpif_flow_stats **stats)
>  {
> @@ -941,6 +946,10 @@ dpif_linux_flow_dump_next(const struct dpif *dpif_ OVS_UNUSED, void *state_,
>          *key = state->flow.key;
>          *key_len = state->flow.key_len;
>      }
> +    if (mask) {
> +        *mask = state->flow.mask;
> +        *mask_len = state->flow.mask ? state->flow.mask_len: 0;
> +    }
>      if (stats) {
>          dpif_linux_flow_get_stats(&state->flow, &state->stats);
>          *stats = &state->stats;
> @@ -1832,6 +1841,7 @@ dpif_linux_flow_from_ofpbuf(struct dpif_linux_flow *flow,
>  {
>      static const struct nl_policy ovs_flow_policy[] = {
>          [OVS_FLOW_ATTR_KEY] = { .type = NL_A_NESTED },
> +        [OVS_FLOW_ATTR_MASK] = { .type = NL_A_NESTED, .optional = true },
>          [OVS_FLOW_ATTR_ACTIONS] = { .type = NL_A_NESTED, .optional = true },
>          [OVS_FLOW_ATTR_STATS] = { NL_POLICY_FOR(struct ovs_flow_stats),
>                                    .optional = true },
> @@ -1863,6 +1873,11 @@ dpif_linux_flow_from_ofpbuf(struct dpif_linux_flow *flow,
>      flow->dp_ifindex = ovs_header->dp_ifindex;
>      flow->key = nl_attr_get(a[OVS_FLOW_ATTR_KEY]);
>      flow->key_len = nl_attr_get_size(a[OVS_FLOW_ATTR_KEY]);
> +
> +    if (a[OVS_FLOW_ATTR_MASK]) {
> +        flow->mask = nl_attr_get(a[OVS_FLOW_ATTR_MASK]);
> +        flow->mask_len = nl_attr_get_size(a[OVS_FLOW_ATTR_MASK]);
> +    }
>      if (a[OVS_FLOW_ATTR_ACTIONS]) {
>          flow->actions = nl_attr_get(a[OVS_FLOW_ATTR_ACTIONS]);
>          flow->actions_len = nl_attr_get_size(a[OVS_FLOW_ATTR_ACTIONS]);
> @@ -1898,6 +1913,10 @@ dpif_linux_flow_to_ofpbuf(const struct dpif_linux_flow *flow,
>          nl_msg_put_unspec(buf, OVS_FLOW_ATTR_KEY, flow->key, flow->key_len);
>      }
>
> +    if (flow->mask_len) {
> +        nl_msg_put_unspec(buf, OVS_FLOW_ATTR_MASK, flow->mask, flow->mask_len);
> +    }
> +
>      if (flow->actions || flow->actions_len) {
>          nl_msg_put_unspec(buf, OVS_FLOW_ATTR_ACTIONS,
>                            flow->actions, flow->actions_len);
> diff --git a/lib/dpif-netdev.c b/lib/dpif-netdev.c
> index 52aedb6..5a54627 100644
> --- a/lib/dpif-netdev.c
> +++ b/lib/dpif-netdev.c
> @@ -879,6 +879,7 @@ dpif_netdev_flow_dump_start(const struct dpif *dpif OVS_UNUSED, void **statep)
>  static int
>  dpif_netdev_flow_dump_next(const struct dpif *dpif, void *state_,
>                             const struct nlattr **key, size_t *key_len,
> +                           const struct nlattr **mask, size_t *mask_len,
>                             const struct nlattr **actions, size_t *actions_len,
>                             const struct dpif_flow_stats **stats)
>  {
> @@ -904,6 +905,11 @@ dpif_netdev_flow_dump_next(const struct dpif *dpif, void *state_,
>          *key_len = buf.size;
>      }
>
> +    if (mask) {
> +        *mask = NULL;
> +        *mask_len = 0;
> +    }
> +
>      if (actions) {
>          free(state->actions);
>          state->actions = xmemdup(flow->actions, flow->actions_len);
> diff --git a/lib/dpif-provider.h b/lib/dpif-provider.h
> index bea822f..0ee0843 100644
> --- a/lib/dpif-provider.h
> +++ b/lib/dpif-provider.h
> @@ -292,6 +292,7 @@ struct dpif_class {
>       * 'flow_dump_next' or 'flow_dump_done' for 'state'. */
>      int (*flow_dump_next)(const struct dpif *dpif, void *state,
>                            const struct nlattr **key, size_t *key_len,
> +                          const struct nlattr **mask, size_t *mask_len,
>                            const struct nlattr **actions, size_t *actions_len,
>                            const struct dpif_flow_stats **stats);
>
> diff --git a/lib/dpif.c b/lib/dpif.c
> index 6aa52d5..f90b02d 100644
> --- a/lib/dpif.c
> +++ b/lib/dpif.c
> @@ -854,6 +854,7 @@ dpif_flow_put__(struct dpif *dpif, const struct dpif_flow_put *put)
>  int
>  dpif_flow_put(struct dpif *dpif, enum dpif_flow_put_flags flags,
>                const struct nlattr *key, size_t key_len,
> +              const struct nlattr *mask, size_t mask_len,
>                const struct nlattr *actions, size_t actions_len,
>                struct dpif_flow_stats *stats)
>  {
> @@ -862,6 +863,8 @@ dpif_flow_put(struct dpif *dpif, enum dpif_flow_put_flags flags,
>      put.flags = flags;
>      put.key = key;
>      put.key_len = key_len;
> +    put.mask = mask;
> +    put.mask_len = mask_len;
>      put.actions = actions;
>      put.actions_len = actions_len;
>      put.stats = stats;
> @@ -937,6 +940,7 @@ dpif_flow_dump_start(struct dpif_flow_dump *dump, const struct dpif *dpif)
>  bool
>  dpif_flow_dump_next(struct dpif_flow_dump *dump,
>                      const struct nlattr **key, size_t *key_len,
> +                    const struct nlattr **mask, size_t *mask_len,
>                      const struct nlattr **actions, size_t *actions_len,
>                      const struct dpif_flow_stats **stats)
>  {
> @@ -946,6 +950,7 @@ dpif_flow_dump_next(struct dpif_flow_dump *dump,
>      if (!error) {
>          error = dpif->dpif_class->flow_dump_next(dpif, dump->state,
>                                                   key, key_len,
> +                                                 mask, mask_len,
>                                                   actions, actions_len,
>                                                   stats);
>          if (error) {
> @@ -957,6 +962,10 @@ dpif_flow_dump_next(struct dpif_flow_dump *dump,
>              *key = NULL;
>              *key_len = 0;
>          }
> +        if (mask) {
> +            *mask = NULL;
> +            *mask_len = 0;
> +        }
>          if (actions) {
>              *actions = NULL;
>              *actions_len = 0;
> diff --git a/lib/dpif.h b/lib/dpif.h
> index fd05b2f..d7b73eb 100644
> --- a/lib/dpif.h
> +++ b/lib/dpif.h
> @@ -447,6 +447,7 @@ enum dpif_flow_put_flags {
>  int dpif_flow_flush(struct dpif *);
>  int dpif_flow_put(struct dpif *, enum dpif_flow_put_flags,
>                    const struct nlattr *key, size_t key_len,
> +                  const struct nlattr *mask, size_t mask_len,
>                    const struct nlattr *actions, size_t actions_len,
>                    struct dpif_flow_stats *);
>  int dpif_flow_del(struct dpif *,
> @@ -464,6 +465,7 @@ struct dpif_flow_dump {
>  void dpif_flow_dump_start(struct dpif_flow_dump *, const struct dpif *);
>  bool dpif_flow_dump_next(struct dpif_flow_dump *,
>                           const struct nlattr **key, size_t *key_len,
> +                         const struct nlattr **mask, size_t *mask_len,
>                           const struct nlattr **actions, size_t *actions_len,
>                           const struct dpif_flow_stats **);
>  int dpif_flow_dump_done(struct dpif_flow_dump *);
> @@ -492,6 +494,8 @@ struct dpif_flow_put {
>      enum dpif_flow_put_flags flags; /* DPIF_FP_*. */
>      const struct nlattr *key;       /* Flow to put. */
>      size_t key_len;                 /* Length of 'key' in bytes. */
> +    const struct nlattr *mask;      /* Mega flow mask to put. */
> +    size_t mask_len;                /* Length of 'mask' in bytes. */
>      const struct nlattr *actions;   /* Actions to perform on flow. */
>      size_t actions_len;             /* Length of 'actions' in bytes. */
>
> diff --git a/lib/netdev-dummy.c b/lib/netdev-dummy.c
> index 3e2187e..1424932 100644
> --- a/lib/netdev-dummy.c
> +++ b/lib/netdev-dummy.c
> @@ -623,7 +623,7 @@ eth_from_packet_or_flow(const char *s)
>       * settle for parsing a datapath key for now.
>       */
>      ofpbuf_init(&odp_key, 0);
> -    error = odp_flow_key_from_string(s, NULL, &odp_key);
> +    error = odp_micro_flow_key_from_string(s, NULL, &odp_key);
>      if (error) {
>          ofpbuf_uninit(&odp_key);
>          return NULL;
> diff --git a/lib/odp-util.c b/lib/odp-util.c
> index acd1a9d..6f4eb96 100644
> --- a/lib/odp-util.c
> +++ b/lib/odp-util.c
> @@ -50,7 +50,10 @@ static const char *delimiters = ", \t\r\n";
>
>  static int parse_odp_key_attr(const char *, const struct simap *port_names,
>                                struct ofpbuf *);
> -static void format_odp_key_attr(const struct nlattr *a, struct ds *ds);
> +static int parse_odp_key_mask_attr(const char *, const struct simap *port_names,
> +                              struct ofpbuf *, struct ofpbuf *);
> +static void format_odp_key_attr(const struct nlattr *a,
> +                                const struct nlattr *ma, struct ds *ds);
>
>  /* Returns one the following for the action with the given OVS_ACTION_ATTR_*
>   * 'type':
> @@ -378,7 +381,7 @@ format_odp_action(struct ds *ds, const struct nlattr *a)
>          break;
>      case OVS_ACTION_ATTR_SET:
>          ds_put_cstr(ds, "set(");
> -        format_odp_key_attr(nl_attr_get(a), ds);
> +        format_odp_key_attr(nl_attr_get(a), NULL, ds);
>          ds_put_cstr(ds, ")");
>          break;
>      case OVS_ACTION_ATTR_PUSH_VLAN:
> @@ -747,10 +750,11 @@ format_generic_odp_key(const struct nlattr *a, struct ds *ds)
>
>          unspec = nl_attr_get(a);
>          for (i = 0; i < len; i++) {
> -            ds_put_char(ds, i ? ' ': '(');
> +            if (i) {
> +                ds_put_char(ds, ' ');
> +            }
>              ds_put_format(ds, "%02x", unspec[i]);
>          }
> -        ds_put_char(ds, ')');
>      }
>  }
>
> @@ -876,55 +880,95 @@ tun_key_to_attr(struct ofpbuf *a, const struct flow_tnl *tun_key)
>  }
>
>  static void
> -format_odp_key_attr(const struct nlattr *a, struct ds *ds)
> +format_odp_key_attr(const struct nlattr *a, const struct nlattr *ma,
> +                    struct ds *ds)
>  {
> -    const struct ovs_key_ethernet *eth_key;
> -    const struct ovs_key_ipv4 *ipv4_key;
> -    const struct ovs_key_ipv6 *ipv6_key;
> -    const struct ovs_key_tcp *tcp_key;
> -    const struct ovs_key_udp *udp_key;
> -    const struct ovs_key_icmp *icmp_key;
> -    const struct ovs_key_icmpv6 *icmpv6_key;
> -    const struct ovs_key_arp *arp_key;
> -    const struct ovs_key_nd *nd_key;
>      struct flow_tnl tun_key;
>      enum ovs_key_attr attr = nl_attr_type(a);
>      char namebuf[OVS_KEY_ATTR_BUFSIZE];
>      int expected_len;
>
> +    if (ma) {
> +        const uint8_t *ma_bytes = nl_attr_get(ma);
> +        bool is_exact = true;
> +        size_t i;
> +
> +        for (i = 0; i < nl_attr_get_size(ma); i++) {
> +            if (ma_bytes[i] != 0xff) {
> +                is_exact = false;
> +                break;
> +            }
> +        }
> +        ma = is_exact ? NULL : ma;
> +    }
> +
>      ds_put_cstr(ds, ovs_key_attr_to_string(attr, namebuf, sizeof namebuf));
> +
>      expected_len = odp_flow_key_attr_len(nl_attr_type(a));
>      if (expected_len != -2 && nl_attr_get_size(a) != expected_len) {
> -        ds_put_format(ds, "(bad length %zu, expected %d)",
> +        ds_put_format(ds, "(bad length %zu, expected %d)(",
>                        nl_attr_get_size(a),
>                        odp_flow_key_attr_len(nl_attr_type(a)));
>          format_generic_odp_key(a, ds);
> +        if (ma) {
> +            ds_put_char(ds, '/');
> +            format_generic_odp_key(ma, ds);
> +        }
> +        ds_put_char(ds, ')');
>          return;
>      }
>
> +    ds_put_char(ds, '(');
>      switch (attr) {
>      case OVS_KEY_ATTR_ENCAP:
> -        ds_put_cstr(ds, "(");
> -        if (nl_attr_get_size(a)) {
> +        if (ma && nl_attr_get_size(ma) && nl_attr_get_size(a)) {
> +            odp_mega_flow_format(nl_attr_get(a), nl_attr_get_size(a),
> +                                 nl_attr_get(ma), nl_attr_get_size(ma),
> +                                 ds);
> +        } else if (nl_attr_get_size(a)) {
>              odp_flow_key_format(nl_attr_get(a), nl_attr_get_size(a), ds);
>          }
> -        ds_put_char(ds, ')');
>          break;
>
>      case OVS_KEY_ATTR_PRIORITY:
> -        ds_put_format(ds, "(%#"PRIx32")", nl_attr_get_u32(a));
> +        ds_put_format(ds, "%#"PRIx32, nl_attr_get_u32(a));
> +        if (ma) {
> +            ds_put_format(ds, "/%#"PRIx32, nl_attr_get_u32(ma));
> +        }
>          break;
>
>      case OVS_KEY_ATTR_SKB_MARK:
> -        ds_put_format(ds, "(%#"PRIx32")", nl_attr_get_u32(a));
> +        ds_put_format(ds, "%#"PRIx32, nl_attr_get_u32(a));
> +        if (ma) {
> +            ds_put_format(ds, "/%#"PRIx32, nl_attr_get_u32(ma));
> +        }
>          break;
>
>      case OVS_KEY_ATTR_TUNNEL:
>          memset(&tun_key, 0, sizeof tun_key);
>          if (odp_tun_key_from_attr(a, &tun_key) == ODP_FIT_ERROR) {
> -            ds_put_format(ds, "(error)");
> +            ds_put_format(ds, "error");
> +        } else if (ma) {
> +            struct flow_tnl tun_mask;
> +
> +            odp_tun_key_from_attr(ma, &tun_mask);
> +
> +            ds_put_format(ds, "tun_id=%#"PRIx64"/%#"PRIx64
> +                          ",src="IP_FMT"/"IP_FMT",dst="IP_FMT"/"IP_FMT
> +                          ",tos=%#"PRIx8"/%#"PRIx8",ttl=%"PRIu8"/%#"PRIx8
> +                          ",flags(",
> +                          ntohll(tun_key.tun_id), ntohll(tun_mask.tun_id),
> +                          IP_ARGS(tun_key.ip_src), IP_ARGS(tun_mask.ip_src),
> +                          IP_ARGS(tun_key.ip_dst), IP_ARGS(tun_mask.ip_dst),
> +                          tun_key.ip_tos, tun_mask.ip_tos,
> +                          tun_key.ip_ttl, tun_mask.ip_ttl);
> +
> +            format_flags(ds, flow_tun_flag_to_string,
> +                         (uint32_t) tun_key.flags, ',');
> +            ds_put_format(ds, "/%#"PRIx16, tun_mask.flags);
> +            ds_put_char(ds, ')');
>          } else {
> -            ds_put_format(ds, "(tun_id=0x%"PRIx64",src="IP_FMT",dst="IP_FMT","
> +            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),
> @@ -933,117 +977,261 @@ format_odp_key_attr(const struct nlattr *a, struct ds *ds)
>
>              format_flags(ds, flow_tun_flag_to_string,
>                           (uint32_t) tun_key.flags, ',');
> -            ds_put_format(ds, "))");
> +            ds_put_char(ds, ')');
>          }
>          break;
>
>      case OVS_KEY_ATTR_IN_PORT:
> -        ds_put_format(ds, "(%"PRIu32")", nl_attr_get_u32(a));
> +        ds_put_format(ds, "%"PRIu32, nl_attr_get_u32(a));
> +        if (ma) {
> +            ds_put_format(ds, "/%#"PRIx32, nl_attr_get_u32(ma));
> +        }
>          break;
>
>      case OVS_KEY_ATTR_ETHERNET:
> -        eth_key = nl_attr_get(a);
> -        ds_put_format(ds, "(src="ETH_ADDR_FMT",dst="ETH_ADDR_FMT")",
> -                      ETH_ADDR_ARGS(eth_key->eth_src),
> -                      ETH_ADDR_ARGS(eth_key->eth_dst));
> +        if (ma) {
> +            const struct ovs_key_ethernet *eth_mask = nl_attr_get(ma);
> +            const struct ovs_key_ethernet *eth_key = nl_attr_get(a);
> +
> +            ds_put_format(ds, "src="ETH_ADDR_FMT"/"ETH_ADDR_FMT
> +                          ",dst="ETH_ADDR_FMT"/"ETH_ADDR_FMT,
> +                          ETH_ADDR_ARGS(eth_key->eth_src),
> +                          ETH_ADDR_ARGS(eth_mask->eth_src),
> +                          ETH_ADDR_ARGS(eth_key->eth_dst),
> +                          ETH_ADDR_ARGS(eth_mask->eth_dst));
> +        } else {
> +            const struct ovs_key_ethernet *eth_key = nl_attr_get(a);
> +
> +            ds_put_format(ds, "src="ETH_ADDR_FMT",dst="ETH_ADDR_FMT,
> +                          ETH_ADDR_ARGS(eth_key->eth_src),
> +                          ETH_ADDR_ARGS(eth_key->eth_dst));
> +        }
>          break;
>
>      case OVS_KEY_ATTR_VLAN:
> -        ds_put_char(ds, '(');
>          format_vlan_tci(ds, nl_attr_get_be16(a));
> -        ds_put_char(ds, ')');
> +        if (ma) {
> +            ds_put_format(ds, "/%#"PRIx16, nl_attr_get_u16(ma));
> +        }
>          break;
>
>      case OVS_KEY_ATTR_MPLS: {
>          const struct ovs_key_mpls *mpls_key = nl_attr_get(a);
> -        ds_put_char(ds, '(');
>          format_mpls_lse(ds, mpls_key->mpls_lse);
> -        ds_put_char(ds, ')');
> +        if (ma) {
> +            ds_put_format(ds, "/%#"PRIx32, nl_attr_get_u32(ma));
> +        }
>          break;
>      }
>
>      case OVS_KEY_ATTR_ETHERTYPE:
> -        ds_put_format(ds, "(0x%04"PRIx16")",
> -                      ntohs(nl_attr_get_be16(a)));
> +        ds_put_format(ds, "0x%04"PRIx16, ntohs(nl_attr_get_be16(a)));
> +        if (ma) {
> +            ds_put_format(ds, "/0x%04"PRIx16, ntohs(nl_attr_get_be16(ma)));
> +        }
>          break;
>
>      case OVS_KEY_ATTR_IPV4:
> -        ipv4_key = nl_attr_get(a);
> -        ds_put_format(ds, "(src="IP_FMT",dst="IP_FMT",proto=%"PRIu8
> -                      ",tos=%#"PRIx8",ttl=%"PRIu8",frag=%s)",
> -                      IP_ARGS(ipv4_key->ipv4_src),
> -                      IP_ARGS(ipv4_key->ipv4_dst),
> -                      ipv4_key->ipv4_proto, ipv4_key->ipv4_tos,
> -                      ipv4_key->ipv4_ttl,
> -                      ovs_frag_type_to_string(ipv4_key->ipv4_frag));
> +        if (ma) {
> +            const struct ovs_key_ipv4 *ipv4_key = nl_attr_get(a);
> +            const struct ovs_key_ipv4 *ipv4_mask = nl_attr_get(ma);
> +
> +            ds_put_format(ds, "src="IP_FMT"/"IP_FMT",dst="IP_FMT"/"IP_FMT
> +                          ",proto=%"PRIu8"/%#"PRIx8",tos=%#"PRIx8"/%#"PRIx8
> +                          ",ttl=%"PRIu8"/%#"PRIx8",frag=%s/%#"PRIx8,
> +                          IP_ARGS(ipv4_key->ipv4_src),
> +                          IP_ARGS(ipv4_mask->ipv4_src),
> +                          IP_ARGS(ipv4_key->ipv4_dst),
> +                          IP_ARGS(ipv4_mask->ipv4_dst),
> +                          ipv4_key->ipv4_proto, ipv4_mask->ipv4_proto,
> +                          ipv4_key->ipv4_tos, ipv4_mask->ipv4_tos,
> +                          ipv4_key->ipv4_ttl, ipv4_mask->ipv4_ttl,
> +                          ovs_frag_type_to_string(ipv4_key->ipv4_frag),
> +                          ipv4_mask->ipv4_frag);
> +        } else {
> +            const struct ovs_key_ipv4 *ipv4_key = nl_attr_get(a);
> +
> +            ds_put_format(ds, "src="IP_FMT",dst="IP_FMT",proto=%"PRIu8
> +                          ",tos=%#"PRIx8",ttl=%"PRIu8",frag=%s",
> +                          IP_ARGS(ipv4_key->ipv4_src),
> +                          IP_ARGS(ipv4_key->ipv4_dst),
> +                          ipv4_key->ipv4_proto, ipv4_key->ipv4_tos,
> +                          ipv4_key->ipv4_ttl,
> +                          ovs_frag_type_to_string(ipv4_key->ipv4_frag));
> +        }
>          break;
>
> -    case OVS_KEY_ATTR_IPV6: {
> -        char src_str[INET6_ADDRSTRLEN];
> -        char dst_str[INET6_ADDRSTRLEN];
> +    case OVS_KEY_ATTR_IPV6:
> +        if (ma) {
> +            const struct ovs_key_ipv6 *ipv6_key, *ipv6_mask;
> +            char src_str[INET6_ADDRSTRLEN];
> +            char dst_str[INET6_ADDRSTRLEN];
> +            char src_mask[INET6_ADDRSTRLEN];
> +            char dst_mask[INET6_ADDRSTRLEN];
> +
> +            ipv6_key = nl_attr_get(a);
> +            inet_ntop(AF_INET6, ipv6_key->ipv6_src, src_str, sizeof src_str);
> +            inet_ntop(AF_INET6, ipv6_key->ipv6_dst, dst_str, sizeof dst_str);
> +
> +            ipv6_mask = nl_attr_get(ma);
> +            inet_ntop(AF_INET6, ipv6_mask->ipv6_src, src_str, sizeof src_str);
> +            inet_ntop(AF_INET6, ipv6_mask->ipv6_dst, dst_str, sizeof dst_str);
> +
> +            ds_put_format(ds, "src=%s/%s,dst=%s/%s,label=%#"PRIx32"/%#"PRIx32
> +                          ",proto=%"PRIu8"/%#"PRIx8",tclass=%#"PRIx8"/%#"PRIx8
> +                          ",hlimit=%"PRIu8"/%#"PRIx8",frag=%s/%#"PRIx8,
> +                          src_str, src_mask, dst_str, dst_mask,
> +                          ntohl(ipv6_key->ipv6_label),
> +                          ntohl(ipv6_mask->ipv6_label),
> +                          ipv6_key->ipv6_proto, ipv6_mask->ipv6_proto,
> +                          ipv6_key->ipv6_tclass, ipv6_mask->ipv6_tclass,
> +                          ipv6_key->ipv6_hlimit, ipv6_mask->ipv6_hlimit,
> +                          ovs_frag_type_to_string(ipv6_key->ipv6_frag),
> +                          ipv6_mask->ipv6_frag);
> +        } else {
> +            const struct ovs_key_ipv6 *ipv6_key;
> +            char src_str[INET6_ADDRSTRLEN];
> +            char dst_str[INET6_ADDRSTRLEN];
>
> -        ipv6_key = nl_attr_get(a);
> -        inet_ntop(AF_INET6, ipv6_key->ipv6_src, src_str, sizeof src_str);
> -        inet_ntop(AF_INET6, ipv6_key->ipv6_dst, dst_str, sizeof dst_str);
> +            ipv6_key = nl_attr_get(a);
> +            inet_ntop(AF_INET6, ipv6_key->ipv6_src, src_str, sizeof src_str);
> +            inet_ntop(AF_INET6, ipv6_key->ipv6_dst, dst_str, sizeof dst_str);
>
> -        ds_put_format(ds, "(src=%s,dst=%s,label=%#"PRIx32",proto=%"PRIu8
> -                      ",tclass=%#"PRIx8",hlimit=%"PRIu8",frag=%s)",
> -                      src_str, dst_str, ntohl(ipv6_key->ipv6_label),
> -                      ipv6_key->ipv6_proto, ipv6_key->ipv6_tclass,
> -                      ipv6_key->ipv6_hlimit,
> -                      ovs_frag_type_to_string(ipv6_key->ipv6_frag));
> +            ds_put_format(ds, "src=%s,dst=%s,label=%#"PRIx32",proto=%"PRIu8
> +                          ",tclass=%#"PRIx8",hlimit=%"PRIu8",frag=%s",
> +                          src_str, dst_str, ntohl(ipv6_key->ipv6_label),
> +                          ipv6_key->ipv6_proto, ipv6_key->ipv6_tclass,
> +                          ipv6_key->ipv6_hlimit,
> +                          ovs_frag_type_to_string(ipv6_key->ipv6_frag));
> +        }
>          break;
> -    }
>
>      case OVS_KEY_ATTR_TCP:
> -        tcp_key = nl_attr_get(a);
> -        ds_put_format(ds, "(src=%"PRIu16",dst=%"PRIu16")",
> -                      ntohs(tcp_key->tcp_src), ntohs(tcp_key->tcp_dst));
> +        if (ma) {
> +            const struct ovs_key_tcp *tcp_mask = nl_attr_get(ma);
> +            const struct ovs_key_tcp *tcp_key = nl_attr_get(a);
> +
> +            ds_put_format(ds, "src=%"PRIu16"/%#"PRIx16
> +                          ",dst=%"PRIu16"/%#"PRIx16,
> +                          ntohs(tcp_key->tcp_src), ntohs(tcp_mask->tcp_src),
> +                          ntohs(tcp_key->tcp_dst), ntohs(tcp_mask->tcp_dst));
> +        } else {
> +            const struct ovs_key_tcp *tcp_key = nl_attr_get(a);
> +
> +            ds_put_format(ds, "src=%"PRIu16",dst=%"PRIu16,
> +                          ntohs(tcp_key->tcp_src), ntohs(tcp_key->tcp_dst));
> +        }
>          break;
>
>      case OVS_KEY_ATTR_UDP:
> -        udp_key = nl_attr_get(a);
> -        ds_put_format(ds, "(src=%"PRIu16",dst=%"PRIu16")",
> -                      ntohs(udp_key->udp_src), ntohs(udp_key->udp_dst));
> +        if (ma) {
> +            const struct ovs_key_udp *udp_mask = nl_attr_get(ma);
> +            const struct ovs_key_udp *udp_key = nl_attr_get(a);
> +
> +            ds_put_format(ds, "src=%"PRIu16"/%#"PRIx16
> +                          ",dst=%"PRIu16"/%#"PRIx16,
> +                          ntohs(udp_key->udp_src), ntohs(udp_mask->udp_src),
> +                          ntohs(udp_key->udp_dst), ntohs(udp_mask->udp_dst));
> +        } else {
> +            const struct ovs_key_udp *udp_key = nl_attr_get(a);
> +
> +            ds_put_format(ds, "src=%"PRIu16",dst=%"PRIu16,
> +                          ntohs(udp_key->udp_src), ntohs(udp_key->udp_dst));
> +        }
>          break;
>
>      case OVS_KEY_ATTR_ICMP:
> -        icmp_key = nl_attr_get(a);
> -        ds_put_format(ds, "(type=%"PRIu8",code=%"PRIu8")",
> -                      icmp_key->icmp_type, icmp_key->icmp_code);
> +        if (ma) {
> +            const struct ovs_key_icmp *icmp_mask = nl_attr_get(ma);
> +            const struct ovs_key_icmp *icmp_key = nl_attr_get(a);
> +
> +            ds_put_format(ds, "type=%"PRIu8"/%#"PRIx8",code=%"PRIu8"/%#"PRIx8,
> +                          icmp_key->icmp_type, icmp_mask->icmp_type,
> +                          icmp_key->icmp_code, icmp_mask->icmp_code);
> +        } else {
> +            const struct ovs_key_icmp *icmp_key = nl_attr_get(a);
> +
> +            ds_put_format(ds, "type=%"PRIu8",code=%"PRIu8,
> +                          icmp_key->icmp_type, icmp_key->icmp_code);
> +        }
>          break;
>
>      case OVS_KEY_ATTR_ICMPV6:
> -        icmpv6_key = nl_attr_get(a);
> -        ds_put_format(ds, "(type=%"PRIu8",code=%"PRIu8")",
> -                      icmpv6_key->icmpv6_type, icmpv6_key->icmpv6_code);
> +        if (ma) {
> +            const struct ovs_key_icmpv6 *icmpv6_mask = nl_attr_get(ma);
> +            const struct ovs_key_icmpv6 *icmpv6_key = nl_attr_get(a);
> +
> +            ds_put_format(ds, "type=%"PRIu8"/%#"PRIx8",code=%"PRIu8"/%#"PRIx8,
> +                          icmpv6_key->icmpv6_type, icmpv6_mask->icmpv6_type,
> +                          icmpv6_key->icmpv6_code, icmpv6_mask->icmpv6_code);
> +        } else {
> +            const struct ovs_key_icmpv6 *icmpv6_key = nl_attr_get(a);
> +
> +            ds_put_format(ds, "type=%"PRIu8",code=%"PRIu8,
> +                          icmpv6_key->icmpv6_type, icmpv6_key->icmpv6_code);
> +        }
>          break;
>
>      case OVS_KEY_ATTR_ARP:
> -        arp_key = nl_attr_get(a);
> -        ds_put_format(ds, "(sip="IP_FMT",tip="IP_FMT",op=%"PRIu16","
> -                      "sha="ETH_ADDR_FMT",tha="ETH_ADDR_FMT")",
> -                      IP_ARGS(arp_key->arp_sip), IP_ARGS(arp_key->arp_tip),
> -                      ntohs(arp_key->arp_op), ETH_ADDR_ARGS(arp_key->arp_sha),
> -                      ETH_ADDR_ARGS(arp_key->arp_tha));
> +        if (ma) {
> +            const struct ovs_key_arp *arp_mask = nl_attr_get(ma);
> +            const struct ovs_key_arp *arp_key = nl_attr_get(a);
> +
> +            ds_put_format(ds, "sip="IP_FMT"/"IP_FMT",tip="IP_FMT"/"IP_FMT
> +                          ",op=%"PRIu16"/%#"PRIx16
> +                          ",sha="ETH_ADDR_FMT"/"ETH_ADDR_FMT
> +                          ",tha="ETH_ADDR_FMT"/"ETH_ADDR_FMT,
> +                          IP_ARGS(arp_key->arp_sip),
> +                          IP_ARGS(arp_mask->arp_sip),
> +                          IP_ARGS(arp_key->arp_tip),
> +                          IP_ARGS(arp_mask->arp_tip),
> +                          ntohs(arp_key->arp_op), ntohs(arp_mask->arp_op),
> +                          ETH_ADDR_ARGS(arp_key->arp_sha),
> +                          ETH_ADDR_ARGS(arp_mask->arp_sha),
> +                          ETH_ADDR_ARGS(arp_key->arp_tha),
> +                          ETH_ADDR_ARGS(arp_mask->arp_tha));
> +        } else {
> +            const struct ovs_key_arp *arp_key = nl_attr_get(a);
> +
> +            ds_put_format(ds, "sip="IP_FMT",tip="IP_FMT",op=%"PRIu16","
> +                          "sha="ETH_ADDR_FMT",tha="ETH_ADDR_FMT,
> +                          IP_ARGS(arp_key->arp_sip), IP_ARGS(arp_key->arp_tip),
> +                          ntohs(arp_key->arp_op),
> +                          ETH_ADDR_ARGS(arp_key->arp_sha),
> +                          ETH_ADDR_ARGS(arp_key->arp_tha));
> +        }
>          break;
>
>      case OVS_KEY_ATTR_ND: {
> +        const struct ovs_key_nd *nd_key, *nd_mask;
>          char target[INET6_ADDRSTRLEN];
>
>          nd_key = nl_attr_get(a);
> +        nd_mask = ma ? nl_attr_get(ma) : NULL;
> +
>          inet_ntop(AF_INET6, nd_key->nd_target, target, sizeof target);
> +        ds_put_format(ds, "target=%s", target);
> +        if (nd_mask) {
> +            inet_ntop(AF_INET6, nd_mask->nd_target, target, sizeof target);
> +            ds_put_format(ds, "/%s", target);
> +        }
>
> -        ds_put_format(ds, "(target=%s", target);
>          if (!eth_addr_is_zero(nd_key->nd_sll)) {
>              ds_put_format(ds, ",sll="ETH_ADDR_FMT,
>                            ETH_ADDR_ARGS(nd_key->nd_sll));
> +            if (nd_mask) {
> +                ds_put_format(ds, "/"ETH_ADDR_FMT,
> +                              ETH_ADDR_ARGS(nd_mask->nd_sll));
> +            }
>          }
>          if (!eth_addr_is_zero(nd_key->nd_tll)) {
>              ds_put_format(ds, ",tll="ETH_ADDR_FMT,
>                            ETH_ADDR_ARGS(nd_key->nd_tll));
> +            if (nd_mask) {
> +                ds_put_format(ds, "/"ETH_ADDR_FMT,
> +                              ETH_ADDR_ARGS(nd_mask->nd_tll));
> +            }
>          }
> -        ds_put_char(ds, ')');
>          break;
>      }
>
> @@ -1051,24 +1239,39 @@ format_odp_key_attr(const struct nlattr *a, struct ds *ds)
>      case __OVS_KEY_ATTR_MAX:
>      default:
>          format_generic_odp_key(a, ds);
> +        if (ma) {
> +            ds_put_char(ds, '/');
> +            format_generic_odp_key(ma, ds);
> +        }
>          break;
>      }
> +    ds_put_char(ds, ')');
>  }
>
>  /* Appends to 'ds' a string representation of the 'key_len' bytes of
> - * OVS_KEY_ATTR_* attributes in 'key'. */
> + * OVS_KEY_ATTR_* attributes in 'key'. If non-null, additionally formats the
> + * 'mask_len' bytes of 'mask' which apply to 'key'. */
>  void
> -odp_flow_key_format(const struct nlattr *key, size_t key_len, struct ds *ds)
> +odp_mega_flow_format(const struct nlattr *key, size_t key_len,
> +                     const struct nlattr *mask, size_t mask_len,
> +                     struct ds *ds)
>  {
>      if (key_len) {
>          const struct nlattr *a;
>          unsigned int left;
>
>          NL_ATTR_FOR_EACH (a, left, key, key_len) {
> +            const struct nlattr *ma = NULL;
> +
>              if (a != key) {
>                  ds_put_char(ds, ',');
>              }
> -            format_odp_key_attr(a, ds);
> +
> +            if (mask && mask_len) {
> +                ma = nl_attr_find__(mask, mask_len, nl_attr_type(a));
> +            }
> +
> +            format_odp_key_attr(a, ma, ds);
>          }
>          if (left) {
>              int i;
> @@ -1087,6 +1290,14 @@ odp_flow_key_format(const struct nlattr *key, size_t key_len, struct ds *ds)
>      }
>  }
>
> +/* Appends to 'ds' a string representation of the 'key_len' bytes of
> + * OVS_KEY_ATTR_* attributes in 'key'. */
> +void
> +odp_flow_key_format(const struct nlattr *key, size_t key_len, struct ds *ds)
> +{
> +    odp_mega_flow_format(key, key_len, NULL, 0, ds);
> +}
> +
>  static int
>  put_nd_key(int n, const char *nd_target_s,
>             const uint8_t *nd_sll, const uint8_t *nd_tll, struct ofpbuf *key)
> @@ -1132,8 +1343,8 @@ mpls_lse_from_components(int mpls_label, int mpls_tc, int mpls_ttl, int mpls_bos
>  }
>
>  static int
> -parse_odp_key_attr(const char *s, const struct simap *port_names,
> -                   struct ofpbuf *key)
> +parse_odp_key_mask_attr(const char *s, const struct simap *port_names,
> +                        struct ofpbuf *key, struct ofpbuf *mask)
>  {
>      /* Many of the sscanf calls in this function use oversized destination
>       * fields because some sscanf() implementations truncate the range of %i
> @@ -1147,31 +1358,79 @@ parse_odp_key_attr(const char *s, const struct simap *port_names,
>
>      {
>          unsigned long long int priority;
> +        unsigned long long int priority_mask;
>          int n = -1;
>
> -        if (sscanf(s, "skb_priority(%llx)%n", &priority, &n) > 0 && n > 0) {
> +        if (sscanf(s, "skb_priority(%llx/%llx)%n", &priority,
> +                   &priority_mask, &n) > 0 && n > 0) {
>              nl_msg_put_u32(key, OVS_KEY_ATTR_PRIORITY, priority);
> +            nl_msg_put_u32(mask, OVS_KEY_ATTR_PRIORITY, priority_mask);
> +            return n;
> +        } else if (sscanf(s, "skb_priority(%llx)%n",
> +                          &priority, &n) > 0 && n > 0) {
> +            nl_msg_put_u32(key, OVS_KEY_ATTR_PRIORITY, priority);
> +            nl_msg_put_u32(mask, OVS_KEY_ATTR_PRIORITY, 0xffffffff);
>              return n;
>          }
>      }
>
>      {
>          unsigned long long int mark;
> +        unsigned long long int mark_mask;
>          int n = -1;
>
> -        if (sscanf(s, "skb_mark(%llx)%n", &mark, &n) > 0 && n > 0) {
> +        if (sscanf(s, "skb_mark(%llx/%llx)%n", &mark,
> +                   &mark_mask, &n) > 0 && n > 0) {
> +            nl_msg_put_u32(key, OVS_KEY_ATTR_SKB_MARK, mark);
> +            nl_msg_put_u32(mask, OVS_KEY_ATTR_SKB_MARK, mark_mask);
> +            return n;
> +        } else if (sscanf(s, "skb_mark(%llx)%n", &mark, &n) > 0 && n > 0) {
>              nl_msg_put_u32(key, OVS_KEY_ATTR_SKB_MARK, mark);
> +            nl_msg_put_u32(mask, OVS_KEY_ATTR_SKB_MARK, 0xffffffff);
>              return n;
>          }
>      }
>
>      {
>          char tun_id_s[32];
> -        int tos, ttl;
> -        struct flow_tnl tun_key;
> +        int tos, tos_mask, ttl, ttl_mask;
> +        struct flow_tnl tun_key, tun_key_mask;
>          int n = -1;
>
>          if (sscanf(s, "tunnel(tun_id=%31[x0123456789abcdefABCDEF],"
> +                   "src="IP_SCAN_FMT"/"IP_SCAN_FMT",dst="IP_SCAN_FMT
> +                   "/"IP_SCAN_FMT",tos=%i/%x,ttl=%i/%x,flags%n",
> +                   tun_id_s,
> +                    IP_SCAN_ARGS(&tun_key.ip_src),
> +                    IP_SCAN_ARGS(&tun_key_mask.ip_src),
> +                    IP_SCAN_ARGS(&tun_key.ip_dst),
> +                    IP_SCAN_ARGS(&tun_key_mask.ip_dst),
> +                    &tos, &tos_mask, &ttl, &ttl_mask,
> +                    &n) > 0 && n > 0) {
> +            int res;
> +            uint32_t flags;
> +
> +            tun_key.tun_id = htonll(strtoull(tun_id_s, NULL, 0));
> +            tun_key.ip_tos = tos;
> +            tun_key_mask.ip_tos = tos_mask;
> +            tun_key.ip_ttl = ttl;
> +            tun_key_mask.ip_ttl = ttl_mask;
> +            res = parse_flags(&s[n], flow_tun_flag_to_string, &flags);
> +            tun_key.flags = (uint16_t) flags;
> +            tun_key_mask.flags = (uint16_t) 0xffff;
> +
> +            if (res < 0) {
> +                return res;
> +            }
> +            n += res;
> +            if (s[n] != ')') {
> +                return -EINVAL;
> +            }
> +            n++;
> +            tun_key_to_attr(key, &tun_key);
> +            tun_key_to_attr(mask, &tun_key_mask);
> +            return n;
> +        } else 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.ip_src),
> @@ -1195,20 +1454,31 @@ parse_odp_key_attr(const char *s, const struct simap *port_names,
>              }
>              n++;
>              tun_key_to_attr(key, &tun_key);
> +
> +            memset(&tun_key, 0xff, sizeof tun_key);
> +            tun_key_to_attr(mask, &tun_key);
>              return n;
>          }
>      }
>
>      {
>          unsigned long long int in_port;
> +        unsigned long long int in_port_mask;
>          int n = -1;
>
> -        if (sscanf(s, "in_port(%lli)%n", &in_port, &n) > 0 && n > 0) {
> +        if (sscanf(s, "in_port(%lli/%llx)%n", &in_port,
> +                   &in_port_mask, &n) > 0 && n > 0) {
> +            nl_msg_put_u32(key, OVS_KEY_ATTR_IN_PORT, in_port);
> +            nl_msg_put_u32(mask, OVS_KEY_ATTR_IN_PORT, in_port_mask);
> +            return n;
> +        } else if (sscanf(s, "in_port(%lli)%n", &in_port, &n) > 0 && n > 0) {
>              nl_msg_put_u32(key, OVS_KEY_ATTR_IN_PORT, in_port);
> +            nl_msg_put_u32(mask, OVS_KEY_ATTR_IN_PORT, 0xffffffff);
>              return n;
>          }
>      }
>
> +
>      if (port_names && !strncmp(s, "in_port(", 8)) {
>          const char *name;
>          const struct simap_node *node;
> @@ -1219,20 +1489,39 @@ parse_odp_key_attr(const char *s, const struct simap *port_names,
>          node = simap_find_len(port_names, name, name_len);
>          if (node) {
>              nl_msg_put_u32(key, OVS_KEY_ATTR_IN_PORT, node->data);
> +            nl_msg_put_u32(mask, OVS_KEY_ATTR_IN_PORT, 0xffffffff);
>              return 8 + name_len + 1;
>          }
>      }
>
>      {
>          struct ovs_key_ethernet eth_key;
> +        struct ovs_key_ethernet eth_key_mask;
>          int n = -1;
>
>          if (sscanf(s,
> +                   "eth(src="ETH_ADDR_SCAN_FMT"/"ETH_ADDR_SCAN_FMT","
> +                        "dst="ETH_ADDR_SCAN_FMT"/"ETH_ADDR_SCAN_FMT")%n",
> +                ETH_ADDR_SCAN_ARGS(eth_key.eth_src),
> +                ETH_ADDR_SCAN_ARGS(eth_key_mask.eth_src),
> +                ETH_ADDR_SCAN_ARGS(eth_key.eth_dst),
> +                ETH_ADDR_SCAN_ARGS(eth_key_mask.eth_dst), &n) > 0 && n > 0) {
> +
> +            nl_msg_put_unspec(key, OVS_KEY_ATTR_ETHERNET,
> +                              &eth_key, sizeof eth_key);
> +            nl_msg_put_unspec(mask, OVS_KEY_ATTR_ETHERNET,
> +                              &eth_key_mask, sizeof eth_key_mask);
> +            return n;
> +        } else if (sscanf(s,
>                     "eth(src="ETH_ADDR_SCAN_FMT",dst="ETH_ADDR_SCAN_FMT")%n",
>                     ETH_ADDR_SCAN_ARGS(eth_key.eth_src),
>                     ETH_ADDR_SCAN_ARGS(eth_key.eth_dst), &n) > 0 && n > 0) {
>              nl_msg_put_unspec(key, OVS_KEY_ATTR_ETHERNET,
>                                &eth_key, sizeof eth_key);
> +
> +            memset(&eth_key, 0xff, sizeof eth_key);
> +            nl_msg_put_unspec(mask, OVS_KEY_ATTR_ETHERNET,
> +                              &eth_key, sizeof eth_key);
>              return n;
>          }
>      }
> @@ -1241,32 +1530,57 @@ parse_odp_key_attr(const char *s, const struct simap *port_names,
>          uint16_t vid;
>          int pcp;
>          int cfi;
> +        unsigned long long int vlan_mask;
>          int n = -1;
>
> -        if ((sscanf(s, "vlan(vid=%"SCNi16",pcp=%i)%n", &vid, &pcp, &n) > 0
> -             && n > 0)) {
> +        if ((sscanf(s, "vlan(vid=%"SCNi16",pcp=%i)/%llx%n",
> +                    &vid, &pcp, &vlan_mask, &n) > 0 && n > 0)) {
> +            nl_msg_put_be16(key, OVS_KEY_ATTR_VLAN,
> +                            htons((vid << VLAN_VID_SHIFT) |
> +                                  (pcp << VLAN_PCP_SHIFT) |
> +                                  VLAN_CFI));
> +            nl_msg_put_be16(mask, OVS_KEY_ATTR_VLAN, htons(vlan_mask));
> +            return n;
> +        } else if ((sscanf(s, "vlan(vid=%"SCNi16",pcp=%i)%n",
> +                           &vid, &pcp, &n) > 0 && n > 0)) {
>              nl_msg_put_be16(key, OVS_KEY_ATTR_VLAN,
>                              htons((vid << VLAN_VID_SHIFT) |
>                                    (pcp << VLAN_PCP_SHIFT) |
>                                    VLAN_CFI));
> +            nl_msg_put_be16(mask, OVS_KEY_ATTR_VLAN, htons(0xffff));
> +            return n;
> +        } else if ((sscanf(s, "vlan(vid=%"SCNi16",pcp=%i,cfi=%i)/%llx%n",
> +                           &vid, &pcp, &cfi, &vlan_mask, &n) > 0 && n > 0)) {
> +            nl_msg_put_be16(key, OVS_KEY_ATTR_VLAN,
> +                            htons((vid << VLAN_VID_SHIFT) |
> +                                  (pcp << VLAN_PCP_SHIFT) |
> +                                  (cfi ? VLAN_CFI : 0)));
> +            nl_msg_put_be16(mask, OVS_KEY_ATTR_VLAN, htons(vlan_mask));
>              return n;
>          } else if ((sscanf(s, "vlan(vid=%"SCNi16",pcp=%i,cfi=%i)%n",
> -                           &vid, &pcp, &cfi, &n) > 0
> -             && n > 0)) {
> +                           &vid, &pcp, &cfi, &n) > 0 && n > 0)) {
>              nl_msg_put_be16(key, OVS_KEY_ATTR_VLAN,
>                              htons((vid << VLAN_VID_SHIFT) |
>                                    (pcp << VLAN_PCP_SHIFT) |
>                                    (cfi ? VLAN_CFI : 0)));
> +            nl_msg_put_be16(mask, OVS_KEY_ATTR_VLAN, htons(0xffff));
>              return n;
>          }
>      }
>
>      {
>          int eth_type;
> +        unsigned long long int eth_type_mask;
>          int n = -1;
>
> -        if (sscanf(s, "eth_type(%i)%n", &eth_type, &n) > 0 && n > 0) {
> +        if (sscanf(s, "eth_type(%i/%llx)%n",
> +                   &eth_type, &eth_type_mask, &n) > 0 && n > 0) {
> +            nl_msg_put_be16(key, OVS_KEY_ATTR_ETHERTYPE, htons(eth_type));
> +            nl_msg_put_be16(mask, OVS_KEY_ATTR_ETHERTYPE, htons(eth_type_mask));
> +            return n;
> +        } else if (sscanf(s, "eth_type(%i)%n", &eth_type, &n) > 0 && n > 0) {
>              nl_msg_put_be16(key, OVS_KEY_ATTR_ETHERTYPE, htons(eth_type));
> +            nl_msg_put_be16(mask, OVS_KEY_ATTR_ETHERTYPE, htons(0xffff));
>              return n;
>          }
>      }
> @@ -1287,17 +1601,54 @@ parse_odp_key_attr(const char *s, const struct simap *port_names,
>          }
>      }
>
> +
>      {
> -        ovs_be32 ipv4_src;
> -        ovs_be32 ipv4_dst;
> +        ovs_be32 ipv4_src, ipv4_src_mask;
> +        ovs_be32 ipv4_dst, ipv4_dst_mask;
>          int ipv4_proto;
> +        unsigned long long int  ipv4_proto_mask;
>          int ipv4_tos;
> +        unsigned long long int  ipv4_tos_mask;
>          int ipv4_ttl;
> +        unsigned long long int  ipv4_ttl_mask;
>          char frag[8];
> +        unsigned long long int  ipv4_frag_mask;
>          enum ovs_frag_type ipv4_frag;
>          int n = -1;
>
> -        if (sscanf(s, "ipv4(src="IP_SCAN_FMT",dst="IP_SCAN_FMT","
> +        if (sscanf(s, "ipv4(src="IP_SCAN_FMT"/"IP_SCAN_FMT","
> +                      "dst="IP_SCAN_FMT"/"IP_SCAN_FMT","
> +                      "proto=%i/%llx,tos=%i/%llx,ttl=%i/%llx,"
> +                      "frag=%7[a-z]/%llx)%n",
> +                      IP_SCAN_ARGS(&ipv4_src), IP_SCAN_ARGS(&ipv4_src_mask),
> +                      IP_SCAN_ARGS(&ipv4_dst), IP_SCAN_ARGS(&ipv4_dst_mask),
> +                      &ipv4_proto, &ipv4_proto_mask,
> +                      &ipv4_tos, &ipv4_tos_mask, &ipv4_ttl, &ipv4_ttl_mask,
> +                      frag, &ipv4_frag_mask, &n) > 0
> +            && n > 0
> +            && ovs_frag_type_from_string(frag, &ipv4_frag)) {
> +            struct ovs_key_ipv4 ipv4_key;
> +            struct ovs_key_ipv4 ipv4_mask;
> +
> +            ipv4_key.ipv4_src = ipv4_src;
> +            ipv4_key.ipv4_dst = ipv4_dst;
> +            ipv4_key.ipv4_proto = ipv4_proto;
> +            ipv4_key.ipv4_tos = ipv4_tos;
> +            ipv4_key.ipv4_ttl = ipv4_ttl;
> +            ipv4_key.ipv4_frag = ipv4_frag;
> +            nl_msg_put_unspec(key, OVS_KEY_ATTR_IPV4,
> +                              &ipv4_key, sizeof ipv4_key);
> +
> +            ipv4_mask.ipv4_src = ipv4_src_mask;
> +            ipv4_mask.ipv4_dst = ipv4_dst_mask;
> +            ipv4_mask.ipv4_proto = ipv4_proto_mask;
> +            ipv4_mask.ipv4_tos = ipv4_tos_mask;
> +            ipv4_mask.ipv4_ttl = ipv4_ttl_mask;
> +            ipv4_mask.ipv4_frag = ipv4_frag_mask;
> +            nl_msg_put_unspec(mask, OVS_KEY_ATTR_IPV4,
> +                              &ipv4_mask, sizeof ipv4_mask);
> +            return n;
> +        } else if (sscanf(s, "ipv4(src="IP_SCAN_FMT",dst="IP_SCAN_FMT","
>                     "proto=%i,tos=%i,ttl=%i,frag=%7[a-z])%n",
>                     IP_SCAN_ARGS(&ipv4_src), IP_SCAN_ARGS(&ipv4_dst),
>                     &ipv4_proto, &ipv4_tos, &ipv4_ttl, frag, &n) > 0
> @@ -1313,22 +1664,68 @@ parse_odp_key_attr(const char *s, const struct simap *port_names,
>              ipv4_key.ipv4_frag = ipv4_frag;
>              nl_msg_put_unspec(key, OVS_KEY_ATTR_IPV4,
>                                &ipv4_key, sizeof ipv4_key);
> +
> +            memset(&ipv4_key, 0xff, sizeof ipv4_key);
> +            nl_msg_put_unspec(mask, OVS_KEY_ATTR_IPV4,
> +                              &ipv4_key, sizeof ipv4_key);
>              return n;
>          }
>      }
>
>      {
>          char ipv6_src_s[IPV6_SCAN_LEN + 1];
> +        char ipv6_src_mask_s[IPV6_SCAN_LEN + 1];
>          char ipv6_dst_s[IPV6_SCAN_LEN + 1];
> +        char ipv6_dst_mask_s[IPV6_SCAN_LEN + 1];
>          int ipv6_label;
> +        unsigned long long int ipv6_label_mask;
>          int ipv6_proto;
> +        unsigned long long int ipv6_proto_mask;
>          int ipv6_tclass;
> +        unsigned long long int ipv6_tclass_mask;
>          int ipv6_hlimit;
> +        unsigned long long int ipv6_hlimit_mask;
>          char frag[8];
>          enum ovs_frag_type ipv6_frag;
> +        unsigned long long int ipv6_frag_mask;
>          int n = -1;
>
> -        if (sscanf(s, "ipv6(src="IPV6_SCAN_FMT",dst="IPV6_SCAN_FMT","
> +        if (sscanf(s, "ipv6(src="IPV6_SCAN_FMT"/"IPV6_SCAN_FMT",dst="
> +                   IPV6_SCAN_FMT"/"IPV6_SCAN_FMT","
> +                   "label=%i/%llx,proto=%i/%llx,tclass=%i/%llx,"
> +                   "hlimit=%i/%llx,frag=%7[a-z]/%llx)%n",
> +                   ipv6_src_s, ipv6_src_mask_s, ipv6_dst_s, ipv6_dst_mask_s,
> +                   &ipv6_label, &ipv6_label_mask, &ipv6_proto,
> +                   &ipv6_proto_mask, &ipv6_tclass, &ipv6_tclass_mask,
> +                   &ipv6_hlimit, &ipv6_hlimit_mask, frag,
> +                   &ipv6_frag_mask, &n) > 0
> +            && n > 0
> +            && ovs_frag_type_from_string(frag, &ipv6_frag)) {
> +            struct ovs_key_ipv6 ipv6_key;
> +            struct ovs_key_ipv6 ipv6_mask;
> +
> +            if (inet_pton(AF_INET6, ipv6_src_s, &ipv6_key.ipv6_src) != 1 ||
> +                inet_pton(AF_INET6, ipv6_dst_s, &ipv6_key.ipv6_dst) != 1) {
> +                return -EINVAL;
> +            }
> +            ipv6_key.ipv6_label = htonl(ipv6_label);
> +            ipv6_key.ipv6_proto = ipv6_proto;
> +            ipv6_key.ipv6_tclass = ipv6_tclass;
> +            ipv6_key.ipv6_hlimit = ipv6_hlimit;
> +            ipv6_key.ipv6_frag = ipv6_frag;
> +            nl_msg_put_unspec(key, OVS_KEY_ATTR_IPV6,
> +                              &ipv6_key, sizeof ipv6_key);
> +
> +            ipv6_mask.ipv6_label = htonl(ipv6_label_mask);
> +            ipv6_mask.ipv6_proto = ipv6_proto_mask;
> +            ipv6_mask.ipv6_tclass = ipv6_tclass_mask;
> +            ipv6_mask.ipv6_hlimit = ipv6_hlimit_mask;
> +            ipv6_mask.ipv6_frag = ipv6_frag_mask;
> +            memset(&ipv6_key, 0xff, sizeof ipv6_key);
> +            nl_msg_put_unspec(mask, OVS_KEY_ATTR_IPV6,
> +                              &ipv6_mask, sizeof ipv6_mask);
> +            return n;
> +        } else if (sscanf(s, "ipv6(src="IPV6_SCAN_FMT",dst="IPV6_SCAN_FMT","
>                     "label=%i,proto=%i,tclass=%i,hlimit=%i,frag=%7[a-z])%n",
>                     ipv6_src_s, ipv6_dst_s, &ipv6_label,
>                     &ipv6_proto, &ipv6_tclass, &ipv6_hlimit, frag, &n) > 0
> @@ -1354,15 +1751,36 @@ parse_odp_key_attr(const char *s, const struct simap *port_names,
>      {
>          int tcp_src;
>          int tcp_dst;
> +        unsigned long long int tcp_src_mask;
> +        unsigned long long int tcp_dst_mask;
>          int n = -1;
>
> -        if (sscanf(s, "tcp(src=%i,dst=%i)%n",&tcp_src, &tcp_dst, &n) > 0
> +        if (sscanf(s, "tcp(src=%i/%llx,dst=%i/%llx)%n",
> +                   &tcp_src, &tcp_src_mask, &tcp_dst, &tcp_dst_mask, &n) > 0
> +            && n > 0) {
> +            struct ovs_key_tcp tcp_key;
> +            struct ovs_key_tcp tcp_mask;
> +
> +            tcp_key.tcp_src = htons(tcp_src);
> +            tcp_key.tcp_dst = htons(tcp_dst);
> +            nl_msg_put_unspec(key, OVS_KEY_ATTR_TCP, &tcp_key, sizeof tcp_key);
> +
> +            tcp_mask.tcp_src = htons(tcp_src_mask);
> +            tcp_mask.tcp_dst = htons(tcp_dst_mask);
> +            nl_msg_put_unspec(mask, OVS_KEY_ATTR_TCP,
> +                              &tcp_mask, sizeof tcp_mask);
> +            return n;
> +        } else if (sscanf(s, "tcp(src=%i,dst=%i)%n",&tcp_src, &tcp_dst, &n) > 0
>              && n > 0) {
>              struct ovs_key_tcp tcp_key;
>
>              tcp_key.tcp_src = htons(tcp_src);
>              tcp_key.tcp_dst = htons(tcp_dst);
>              nl_msg_put_unspec(key, OVS_KEY_ATTR_TCP, &tcp_key, sizeof tcp_key);
> +
> +            memset(&tcp_key, 0xff, sizeof tcp_key);
> +            nl_msg_put_unspec(mask, OVS_KEY_ATTR_TCP,
> +                              &tcp_key, sizeof tcp_key);
>              return n;
>          }
>      }
> @@ -1370,8 +1788,26 @@ parse_odp_key_attr(const char *s, const struct simap *port_names,
>      {
>          int udp_src;
>          int udp_dst;
> +        unsigned long long int udp_src_mask;
> +        unsigned long long int udp_dst_mask;
>          int n = -1;
>
> +        if (sscanf(s, "udp(src=%i/%llx,dst=%i/%llx)%n",
> +                   &udp_src, &udp_src_mask,
> +                   &udp_dst, &udp_dst_mask, &n) > 0 && n > 0) {
> +            struct ovs_key_udp udp_key;
> +            struct ovs_key_udp udp_mask;
> +
> +            udp_key.udp_src = htons(udp_src);
> +            udp_key.udp_dst = htons(udp_dst);
> +            nl_msg_put_unspec(key, OVS_KEY_ATTR_UDP, &udp_key, sizeof udp_key);
> +
> +            udp_mask.udp_src = htons(udp_src_mask);
> +            udp_mask.udp_dst = htons(udp_dst_mask);
> +            nl_msg_put_unspec(mask, OVS_KEY_ATTR_UDP,
> +                              &udp_mask, sizeof udp_mask);
> +            return n;
> +        }
>          if (sscanf(s, "udp(src=%i,dst=%i)%n", &udp_src, &udp_dst, &n) > 0
>              && n > 0) {
>              struct ovs_key_udp udp_key;
> @@ -1379,6 +1815,9 @@ parse_odp_key_attr(const char *s, const struct simap *port_names,
>              udp_key.udp_src = htons(udp_src);
>              udp_key.udp_dst = htons(udp_dst);
>              nl_msg_put_unspec(key, OVS_KEY_ATTR_UDP, &udp_key, sizeof udp_key);
> +
> +            memset(&udp_key, 0xff, sizeof udp_key);
> +            nl_msg_put_unspec(mask, OVS_KEY_ATTR_UDP, &udp_key, sizeof udp_key);
>              return n;
>          }
>      }
> @@ -1386,9 +1825,27 @@ parse_odp_key_attr(const char *s, const struct simap *port_names,
>      {
>          int icmp_type;
>          int icmp_code;
> +        unsigned long long int icmp_type_mask;
> +        unsigned long long int icmp_code_mask;
>          int n = -1;
>
> -        if (sscanf(s, "icmp(type=%i,code=%i)%n",
> +        if (sscanf(s, "icmp(type=%i/%llx,code=%i/%llx)%n",
> +                   &icmp_type, &icmp_type_mask,
> +                   &icmp_code, &icmp_code_mask, &n) > 0 && n > 0) {
> +            struct ovs_key_icmp icmp_key;
> +            struct ovs_key_icmp icmp_mask;
> +
> +            icmp_key.icmp_type = icmp_type;
> +            icmp_key.icmp_code = icmp_code;
> +            nl_msg_put_unspec(key, OVS_KEY_ATTR_ICMP,
> +                              &icmp_key, sizeof icmp_key);
> +
> +            icmp_mask.icmp_type = icmp_type_mask;
> +            icmp_mask.icmp_code = icmp_code_mask;
> +            nl_msg_put_unspec(mask, OVS_KEY_ATTR_ICMP,
> +                              &icmp_mask, sizeof icmp_mask);
> +            return n;
> +        } else if (sscanf(s, "icmp(type=%i,code=%i)%n",
>                     &icmp_type, &icmp_code, &n) > 0
>              && n > 0) {
>              struct ovs_key_icmp icmp_key;
> @@ -1397,32 +1854,87 @@ parse_odp_key_attr(const char *s, const struct simap *port_names,
>              icmp_key.icmp_code = icmp_code;
>              nl_msg_put_unspec(key, OVS_KEY_ATTR_ICMP,
>                                &icmp_key, sizeof icmp_key);
> +            memset(&icmp_key, 0xff, sizeof icmp_key);
> +            nl_msg_put_unspec(mask, OVS_KEY_ATTR_ICMP, &icmp_key,
> +                              sizeof icmp_key);
>              return n;
>          }
>      }
>
>      {
>          struct ovs_key_icmpv6 icmpv6_key;
> +        struct ovs_key_icmpv6 icmpv6_mask;
> +        unsigned long long int icmpv6_type_mask;
> +        unsigned long long int icmpv6_code_mask;
>          int n = -1;
>
> -        if (sscanf(s, "icmpv6(type=%"SCNi8",code=%"SCNi8")%n",
> +        if (sscanf(s, "icmpv6(type=%"SCNi8"/%llx,code=%"SCNi8"/%llx)%n",
> +                   &icmpv6_key.icmpv6_type, &icmpv6_type_mask,
> +                   &icmpv6_key.icmpv6_code, &icmpv6_code_mask, &n) > 0
> +            && n > 0) {
> +            nl_msg_put_unspec(key, OVS_KEY_ATTR_ICMPV6,
> +                              &icmpv6_key, sizeof icmpv6_key);
> +
> +            icmpv6_mask.icmpv6_type = icmpv6_type_mask;
> +            icmpv6_mask.icmpv6_code = icmpv6_code_mask;
> +            nl_msg_put_unspec(mask, OVS_KEY_ATTR_ICMPV6, &icmpv6_mask,
> +                              sizeof icmpv6_mask);
> +            return n;
> +        } else if (sscanf(s, "icmpv6(type=%"SCNi8",code=%"SCNi8")%n",
>                     &icmpv6_key.icmpv6_type, &icmpv6_key.icmpv6_code,&n) > 0
>              && n > 0) {
>              nl_msg_put_unspec(key, OVS_KEY_ATTR_ICMPV6,
>                                &icmpv6_key, sizeof icmpv6_key);
> +
> +            memset(&icmpv6_key, 0xff, sizeof icmpv6_key);
> +            nl_msg_put_unspec(mask, OVS_KEY_ATTR_ICMPV6, &icmpv6_key,
> +                              sizeof icmpv6_key);
>              return n;
>          }
>      }
>
>      {
> -        ovs_be32 arp_sip;
> -        ovs_be32 arp_tip;
> +        ovs_be32 arp_sip, arp_sip_mask;
> +        ovs_be32 arp_tip, arp_tip_mask;
>          int arp_op;
> +        unsigned long long int  arp_op_mask;
>          uint8_t arp_sha[ETH_ADDR_LEN];
> +        uint8_t arp_sha_mask[ETH_ADDR_LEN];
>          uint8_t arp_tha[ETH_ADDR_LEN];
> +        uint8_t arp_tha_mask[ETH_ADDR_LEN];
>          int n = -1;
>
> -        if (sscanf(s, "arp(sip="IP_SCAN_FMT",tip="IP_SCAN_FMT","
> +        if (sscanf(s, "arp(sip="IP_SCAN_FMT"/"IP_SCAN_FMT","
> +                   "tip="IP_SCAN_FMT"/"IP_SCAN_FMT","
> +                   "op=%i/%llx,sha="ETH_ADDR_SCAN_FMT"/"ETH_ADDR_SCAN_FMT","
> +                   "tha="ETH_ADDR_SCAN_FMT"/"ETH_ADDR_SCAN_FMT")%n",
> +                   IP_SCAN_ARGS(&arp_sip), IP_SCAN_ARGS(&arp_sip_mask),
> +                   IP_SCAN_ARGS(&arp_tip), IP_SCAN_ARGS(&arp_tip_mask),
> +                   &arp_op, &arp_op_mask,
> +                   ETH_ADDR_SCAN_ARGS(arp_sha),
> +                   ETH_ADDR_SCAN_ARGS(arp_sha_mask),
> +                   ETH_ADDR_SCAN_ARGS(arp_tha),
> +                   ETH_ADDR_SCAN_ARGS(arp_tha_mask), &n) > 0 && n > 0) {
> +            struct ovs_key_arp arp_key;
> +            struct ovs_key_arp arp_mask;
> +
> +            memset(&arp_key, 0, sizeof arp_key);
> +            arp_key.arp_sip = arp_sip;
> +            arp_key.arp_tip = arp_tip;
> +            arp_key.arp_op = htons(arp_op);
> +            memcpy(arp_key.arp_sha, arp_sha, ETH_ADDR_LEN);
> +            memcpy(arp_key.arp_tha, arp_tha, ETH_ADDR_LEN);
> +            nl_msg_put_unspec(key, OVS_KEY_ATTR_ARP, &arp_key, sizeof arp_key);
> +
> +            arp_mask.arp_sip = arp_sip_mask;
> +            arp_mask.arp_tip = arp_tip_mask;
> +            arp_mask.arp_op = htons(arp_op_mask);
> +            memcpy(arp_mask.arp_sha, arp_sha_mask, ETH_ADDR_LEN);
> +            memcpy(arp_mask.arp_tha, arp_tha_mask, ETH_ADDR_LEN);
> +            nl_msg_put_unspec(mask, OVS_KEY_ATTR_ARP,
> +                              &arp_mask, sizeof arp_mask);
> +            return n;
> +        } else if (sscanf(s, "arp(sip="IP_SCAN_FMT",tip="IP_SCAN_FMT","
>                     "op=%i,sha="ETH_ADDR_SCAN_FMT",tha="ETH_ADDR_SCAN_FMT")%n",
>                     IP_SCAN_ARGS(&arp_sip),
>                     IP_SCAN_ARGS(&arp_tip),
> @@ -1438,44 +1950,88 @@ parse_odp_key_attr(const char *s, const struct simap *port_names,
>              memcpy(arp_key.arp_sha, arp_sha, ETH_ADDR_LEN);
>              memcpy(arp_key.arp_tha, arp_tha, ETH_ADDR_LEN);
>              nl_msg_put_unspec(key, OVS_KEY_ATTR_ARP, &arp_key, sizeof arp_key);
> +
> +            memset(&arp_key, 0xff, sizeof arp_key);
> +            nl_msg_put_unspec(mask, OVS_KEY_ATTR_ARP, &arp_key, sizeof arp_key);
>              return n;
>          }
>      }
>
>      {
>          char nd_target_s[IPV6_SCAN_LEN + 1];
> +        char nd_target_mask_s[IPV6_SCAN_LEN + 1];
>          uint8_t nd_sll[ETH_ADDR_LEN];
> +        uint8_t nd_sll_mask[ETH_ADDR_LEN];
>          uint8_t nd_tll[ETH_ADDR_LEN];
> +        uint8_t nd_tll_mask[ETH_ADDR_LEN];
>          int n = -1;
>
> -        if (sscanf(s, "nd(target="IPV6_SCAN_FMT")%n",
> +        memset(&nd_target_mask_s[0], 0xff, sizeof nd_target_s);
> +        memset(&nd_sll_mask[0], 0xff, sizeof nd_sll);
> +        memset(&nd_tll_mask [0], 0xff, sizeof nd_tll);
> +
> +        if (sscanf(s, "nd(target="IPV6_SCAN_FMT"/"IPV6_SCAN_FMT")%n",
> +                   nd_target_s, nd_target_mask_s, &n) > 0 && n > 0) {
> +                put_nd_key(n, nd_target_s, NULL, NULL, key);
> +                put_nd_key(n, nd_target_mask_s, NULL, NULL, mask);
> +        } else if (sscanf(s, "nd(target="IPV6_SCAN_FMT")%n",
>                     nd_target_s, &n) > 0 && n > 0) {
> -            return put_nd_key(n, nd_target_s, NULL, NULL, key);
> -        }
> -        if (sscanf(s, "nd(target="IPV6_SCAN_FMT",sll="ETH_ADDR_SCAN_FMT")%n",
> +                put_nd_key(n, nd_target_s, NULL, NULL, key);
> +                put_nd_key(n, nd_target_mask_s, NULL, NULL, mask);
> +        } else if (sscanf(s, "nd(target="IPV6_SCAN_FMT"/"IPV6_SCAN_FMT
> +                         ",sll="ETH_ADDR_SCAN_FMT"/"ETH_ADDR_SCAN_FMT")%n",
> +                   nd_target_s, nd_target_mask_s,
> +                   ETH_ADDR_SCAN_ARGS(nd_sll),
> +                   ETH_ADDR_SCAN_ARGS(nd_sll_mask), &n) > 0 && n > 0) {
> +            put_nd_key(n, nd_target_s, nd_sll, NULL, key);
> +            put_nd_key(n, nd_target_mask_s, nd_sll_mask, NULL, mask);
> +        } else if (sscanf(s, "nd(target="IPV6_SCAN_FMT",sll="ETH_ADDR_SCAN_FMT")%n",
>                     nd_target_s, ETH_ADDR_SCAN_ARGS(nd_sll), &n) > 0
>              && n > 0) {
> -            return put_nd_key(n, nd_target_s, nd_sll, NULL, key);
> -        }
> -        if (sscanf(s, "nd(target="IPV6_SCAN_FMT",tll="ETH_ADDR_SCAN_FMT")%n",
> +            put_nd_key(n, nd_target_s, nd_sll, NULL, key);
> +            put_nd_key(n, nd_target_mask_s, nd_sll_mask, NULL, mask);
> +        } else if (sscanf(s, "nd(target="IPV6_SCAN_FMT"/"IPV6_SCAN_FMT
> +                         ",tll="ETH_ADDR_SCAN_FMT"/"ETH_ADDR_SCAN_FMT")%n",
> +                   nd_target_s, nd_target_mask_s,
> +                   ETH_ADDR_SCAN_ARGS(nd_tll),
> +                   ETH_ADDR_SCAN_ARGS(nd_tll_mask), &n) > 0 && n > 0) {
> +            put_nd_key(n, nd_target_s, NULL, nd_tll, key);
> +            put_nd_key(n, nd_target_mask_s, NULL, nd_tll_mask, mask);
> +        } else if (sscanf(s, "nd(target="IPV6_SCAN_FMT",tll="ETH_ADDR_SCAN_FMT")%n",
>                     nd_target_s, ETH_ADDR_SCAN_ARGS(nd_tll), &n) > 0
>              && n > 0) {
> -            return put_nd_key(n, nd_target_s, NULL, nd_tll, key);
> -        }
> -        if (sscanf(s, "nd(target="IPV6_SCAN_FMT",sll="ETH_ADDR_SCAN_FMT","
> +            put_nd_key(n, nd_target_s, NULL, nd_tll, key);
> +            put_nd_key(n, nd_target_mask_s, NULL, nd_tll_mask, mask);
> +        } else if (sscanf(s, "nd(target="IPV6_SCAN_FMT"/"IPV6_SCAN_FMT
> +                   ",sll="ETH_ADDR_SCAN_FMT"/"ETH_ADDR_SCAN_FMT","
> +                   "tll="ETH_ADDR_SCAN_FMT"/"ETH_ADDR_SCAN_FMT")%n",
> +                   nd_target_s, nd_target_mask_s,
> +                   ETH_ADDR_SCAN_ARGS(nd_sll), ETH_ADDR_SCAN_ARGS(nd_sll_mask),
> +                   ETH_ADDR_SCAN_ARGS(nd_tll), ETH_ADDR_SCAN_ARGS(nd_tll_mask),
> +                   &n) > 0
> +            && n > 0) {
> +            put_nd_key(n, nd_target_s, nd_sll, nd_tll, key);
> +            put_nd_key(n, nd_target_mask_s, nd_sll_mask, nd_tll_mask, mask);
> +        } else if (sscanf(s, "nd(target="IPV6_SCAN_FMT",sll="ETH_ADDR_SCAN_FMT","
>                     "tll="ETH_ADDR_SCAN_FMT")%n",
>                     nd_target_s, ETH_ADDR_SCAN_ARGS(nd_sll),
>                     ETH_ADDR_SCAN_ARGS(nd_tll), &n) > 0
>              && n > 0) {
> -            return put_nd_key(n, nd_target_s, nd_sll, nd_tll, key);
> +            put_nd_key(n, nd_target_s, nd_sll, nd_tll, key);
> +            put_nd_key(n, nd_target_mask_s, nd_sll_mask, nd_tll_mask, mask);
>          }
> +
> +        if (n != -1)
> +            return n;
> +
>      }
>
>      if (!strncmp(s, "encap(", 6)) {
>          const char *start = s;
> -        size_t encap;
> +        size_t encap, encap_mask;
>
>          encap = nl_msg_start_nested(key, OVS_KEY_ATTR_ENCAP);
> +        encap_mask = nl_msg_start_nested(mask, OVS_KEY_ATTR_ENCAP);
>
>          s += 6;
>          for (;;) {
> @@ -1488,7 +2044,7 @@ parse_odp_key_attr(const char *s, const struct simap *port_names,
>                  break;
>              }
>
> -            retval = parse_odp_key_attr(s, port_names, key);
> +            retval = parse_odp_key_mask_attr(s, port_names, key, mask);
>              if (retval < 0) {
>                  return retval;
>              }
> @@ -1497,6 +2053,7 @@ parse_odp_key_attr(const char *s, const struct simap *port_names,
>          s++;
>
>          nl_msg_end_nested(key, encap);
> +        nl_msg_end_nested(mask, encap_mask);
>
>          return s - start;
>      }
> @@ -1504,25 +2061,398 @@ parse_odp_key_attr(const char *s, const struct simap *port_names,
>      return -EINVAL;
>  }
>
> -/* Parses the string representation of a datapath flow key, in the
> - * format output by odp_flow_key_format().  Returns 0 if successful,
> - * otherwise a positive errno value.  On success, the flow key is
> - * appended to 'key' as a series of Netlink attributes.  On failure, no
> - * data is appended to 'key'.  Either way, 'key''s data might be
> - * reallocated.
> - *
> - * If 'port_names' is nonnull, it points to an simap that maps from a port name
> - * to a port number.  (Port names may be used instead of port numbers in
> - * in_port.)
> - *
> - * On success, the attributes appended to 'key' are individually syntactically
> - * valid, but they may not be valid as a sequence.  'key' might, for example,
> - * have duplicated keys.  odp_flow_key_to_flow() will detect those errors. */
> -int
> -odp_flow_key_from_string(const char *s, const struct simap *port_names,
> -                         struct ofpbuf *key)
> -{
> -    const size_t old_size = key->size;
> +static int
> +parse_odp_key_attr(const char *s, const struct simap *port_names,
> +                   struct ofpbuf *key)
> +{
> +    /* Many of the sscanf calls in this function use oversized destination
> +     * fields because some sscanf() implementations truncate the range of %i
> +     * directives, so that e.g. "%"SCNi16 interprets input of "0xfedc" as a
> +     * value of 0x7fff.  The other alternatives are to allow only a single
> +     * radix (e.g. decimal or hexadecimal) or to write more sophisticated
> +     * parsers.
> +     *
> +     * The tun_id parser has to use an alternative approach because there is no
> +     * type larger than 64 bits. */
> +
> +    {
> +        unsigned long long int priority;
> +        int n = -1;
> +
> +        if (sscanf(s, "skb_priority(%llx)%n", &priority, &n) > 0 && n > 0) {
> +            nl_msg_put_u32(key, OVS_KEY_ATTR_PRIORITY, priority);
> +            return n;
> +        }
> +    }
> +
> +    {
> +        unsigned long long int mark;
> +        int n = -1;
> +
> +        if (sscanf(s, "skb_mark(%llx)%n", &mark, &n) > 0 && n > 0) {
> +            nl_msg_put_u32(key, OVS_KEY_ATTR_SKB_MARK, mark);
> +            return n;
> +        }
> +    }
> +
> +    {
> +        char tun_id_s[32];
> +        int tos, ttl;
> +        struct flow_tnl tun_key;
> +        int n = -1;
> +
> +        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.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.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;
> +
> +            if (res < 0) {
> +                return res;
> +            }
> +            n += res;
> +            if (s[n] != ')') {
> +                return -EINVAL;
> +            }
> +            n++;
> +            tun_key_to_attr(key, &tun_key);
> +            return n;
> +        }
> +    }
> +
> +    {
> +        unsigned long long int in_port;
> +        int n = -1;
> +
> +        if (sscanf(s, "in_port(%lli)%n", &in_port, &n) > 0 && n > 0) {
> +            nl_msg_put_u32(key, OVS_KEY_ATTR_IN_PORT, in_port);
> +            return n;
> +        }
> +    }
> +
> +    if (port_names && !strncmp(s, "in_port(", 8)) {
> +        const char *name;
> +        const struct simap_node *node;
> +        int name_len;
> +
> +        name = s + 8;
> +        name_len = strcspn(s, ")");
> +        node = simap_find_len(port_names, name, name_len);
> +        if (node) {
> +            nl_msg_put_u32(key, OVS_KEY_ATTR_IN_PORT, node->data);
> +            return 8 + name_len + 1;
> +        }
> +    }
> +
> +    {
> +        int label, tc, ttl, bos;
> +        int n = -1;
> +
> +        if (sscanf(s, "mpls(label=%"SCNi32",tc=%i,ttl=%i,bos=%i)%n",
> +                    &label, &tc, &ttl, &bos, &n) > 0 &&
> +                    n > 0) {
> +            struct ovs_key_mpls *mpls;
> +
> +            mpls = nl_msg_put_unspec_uninit(key, OVS_KEY_ATTR_MPLS,
> +                                            sizeof *mpls);
> +            mpls->mpls_lse = mpls_lse_from_components(label, tc, ttl, bos);
> +            return n;
> +        }
> +    }
> +
> +    {
> +        struct ovs_key_ethernet eth_key;
> +        int n = -1;
> +
> +        if (sscanf(s,
> +                   "eth(src="ETH_ADDR_SCAN_FMT",dst="ETH_ADDR_SCAN_FMT")%n",
> +                   ETH_ADDR_SCAN_ARGS(eth_key.eth_src),
> +                   ETH_ADDR_SCAN_ARGS(eth_key.eth_dst), &n) > 0 && n > 0) {
> +            nl_msg_put_unspec(key, OVS_KEY_ATTR_ETHERNET,
> +                              &eth_key, sizeof eth_key);
> +            return n;
> +        }
> +    }
> +
> +    {
> +        uint16_t vid;
> +        int pcp;
> +        int cfi;
> +        int n = -1;
> +
> +        if ((sscanf(s, "vlan(vid=%"SCNi16",pcp=%i)%n", &vid, &pcp, &n) > 0
> +             && n > 0)) {
> +            nl_msg_put_be16(key, OVS_KEY_ATTR_VLAN,
> +                            htons((vid << VLAN_VID_SHIFT) |
> +                                  (pcp << VLAN_PCP_SHIFT) |
> +                                  VLAN_CFI));
> +            return n;
> +        } else if ((sscanf(s, "vlan(vid=%"SCNi16",pcp=%i,cfi=%i)%n",
> +                           &vid, &pcp, &cfi, &n) > 0
> +             && n > 0)) {
> +            nl_msg_put_be16(key, OVS_KEY_ATTR_VLAN,
> +                            htons((vid << VLAN_VID_SHIFT) |
> +                                  (pcp << VLAN_PCP_SHIFT) |
> +                                  (cfi ? VLAN_CFI : 0)));
> +            return n;
> +        }
> +    }
> +
> +    {
> +        int eth_type;
> +        int n = -1;
> +
> +        if (sscanf(s, "eth_type(%i)%n", &eth_type, &n) > 0 && n > 0) {
> +            nl_msg_put_be16(key, OVS_KEY_ATTR_ETHERTYPE, htons(eth_type));
> +            return n;
> +        }
> +    }
> +
> +    {
> +        ovs_be32 ipv4_src;
> +        ovs_be32 ipv4_dst;
> +        int ipv4_proto;
> +        int ipv4_tos;
> +        int ipv4_ttl;
> +        char frag[8];
> +        enum ovs_frag_type ipv4_frag;
> +        int n = -1;
> +
> +        if (sscanf(s, "ipv4(src="IP_SCAN_FMT",dst="IP_SCAN_FMT","
> +                   "proto=%i,tos=%i,ttl=%i,frag=%7[a-z])%n",
> +                   IP_SCAN_ARGS(&ipv4_src), IP_SCAN_ARGS(&ipv4_dst),
> +                   &ipv4_proto, &ipv4_tos, &ipv4_ttl, frag, &n) > 0
> +            && n > 0
> +            && ovs_frag_type_from_string(frag, &ipv4_frag)) {
> +            struct ovs_key_ipv4 ipv4_key;
> +
> +            ipv4_key.ipv4_src = ipv4_src;
> +            ipv4_key.ipv4_dst = ipv4_dst;
> +            ipv4_key.ipv4_proto = ipv4_proto;
> +            ipv4_key.ipv4_tos = ipv4_tos;
> +            ipv4_key.ipv4_ttl = ipv4_ttl;
> +            ipv4_key.ipv4_frag = ipv4_frag;
> +            nl_msg_put_unspec(key, OVS_KEY_ATTR_IPV4,
> +                              &ipv4_key, sizeof ipv4_key);
> +            return n;
> +        }
> +    }
> +
> +    {
> +        char ipv6_src_s[IPV6_SCAN_LEN + 1];
> +        char ipv6_dst_s[IPV6_SCAN_LEN + 1];
> +        int ipv6_label;
> +        int ipv6_proto;
> +        int ipv6_tclass;
> +        int ipv6_hlimit;
> +        char frag[8];
> +        enum ovs_frag_type ipv6_frag;
> +        int n = -1;
> +
> +        if (sscanf(s, "ipv6(src="IPV6_SCAN_FMT",dst="IPV6_SCAN_FMT","
> +                   "label=%i,proto=%i,tclass=%i,hlimit=%i,frag=%7[a-z])%n",
> +                   ipv6_src_s, ipv6_dst_s, &ipv6_label,
> +                   &ipv6_proto, &ipv6_tclass, &ipv6_hlimit, frag, &n) > 0
> +            && n > 0
> +            && ovs_frag_type_from_string(frag, &ipv6_frag)) {
> +            struct ovs_key_ipv6 ipv6_key;
> +
> +            if (inet_pton(AF_INET6, ipv6_src_s, &ipv6_key.ipv6_src) != 1 ||
> +                inet_pton(AF_INET6, ipv6_dst_s, &ipv6_key.ipv6_dst) != 1) {
> +                return -EINVAL;
> +            }
> +            ipv6_key.ipv6_label = htonl(ipv6_label);
> +            ipv6_key.ipv6_proto = ipv6_proto;
> +            ipv6_key.ipv6_tclass = ipv6_tclass;
> +            ipv6_key.ipv6_hlimit = ipv6_hlimit;
> +            ipv6_key.ipv6_frag = ipv6_frag;
> +            nl_msg_put_unspec(key, OVS_KEY_ATTR_IPV6,
> +                              &ipv6_key, sizeof ipv6_key);
> +            return n;
> +        }
> +    }
> +
> +    {
> +        int tcp_src;
> +        int tcp_dst;
> +        int n = -1;
> +
> +        if (sscanf(s, "tcp(src=%i,dst=%i)%n",&tcp_src, &tcp_dst, &n) > 0
> +            && n > 0) {
> +            struct ovs_key_tcp tcp_key;
> +
> +            tcp_key.tcp_src = htons(tcp_src);
> +            tcp_key.tcp_dst = htons(tcp_dst);
> +            nl_msg_put_unspec(key, OVS_KEY_ATTR_TCP, &tcp_key, sizeof tcp_key);
> +            return n;
> +        }
> +    }
> +
> +    {
> +        int udp_src;
> +        int udp_dst;
> +        int n = -1;
> +
> +        if (sscanf(s, "udp(src=%i,dst=%i)%n", &udp_src, &udp_dst, &n) > 0
> +            && n > 0) {
> +            struct ovs_key_udp udp_key;
> +
> +            udp_key.udp_src = htons(udp_src);
> +            udp_key.udp_dst = htons(udp_dst);
> +            nl_msg_put_unspec(key, OVS_KEY_ATTR_UDP, &udp_key, sizeof udp_key);
> +            return n;
> +        }
> +    }
> +
> +    {
> +        int icmp_type;
> +        int icmp_code;
> +        int n = -1;
> +
> +        if (sscanf(s, "icmp(type=%i,code=%i)%n",
> +                   &icmp_type, &icmp_code, &n) > 0
> +            && n > 0) {
> +            struct ovs_key_icmp icmp_key;
> +
> +            icmp_key.icmp_type = icmp_type;
> +            icmp_key.icmp_code = icmp_code;
> +            nl_msg_put_unspec(key, OVS_KEY_ATTR_ICMP,
> +                              &icmp_key, sizeof icmp_key);
> +            return n;
> +        }
> +    }
> +
> +    {
> +        struct ovs_key_icmpv6 icmpv6_key;
> +        int n = -1;
> +
> +        if (sscanf(s, "icmpv6(type=%"SCNi8",code=%"SCNi8")%n",
> +                   &icmpv6_key.icmpv6_type, &icmpv6_key.icmpv6_code,&n) > 0
> +            && n > 0) {
> +            nl_msg_put_unspec(key, OVS_KEY_ATTR_ICMPV6,
> +                              &icmpv6_key, sizeof icmpv6_key);
> +            return n;
> +        }
> +    }
> +
> +    {
> +        ovs_be32 arp_sip;
> +        ovs_be32 arp_tip;
> +        int arp_op;
> +        uint8_t arp_sha[ETH_ADDR_LEN];
> +        uint8_t arp_tha[ETH_ADDR_LEN];
> +        int n = -1;
> +
> +        if (sscanf(s, "arp(sip="IP_SCAN_FMT",tip="IP_SCAN_FMT","
> +                   "op=%i,sha="ETH_ADDR_SCAN_FMT",tha="ETH_ADDR_SCAN_FMT")%n",
> +                   IP_SCAN_ARGS(&arp_sip),
> +                   IP_SCAN_ARGS(&arp_tip),
> +                   &arp_op,
> +                   ETH_ADDR_SCAN_ARGS(arp_sha),
> +                   ETH_ADDR_SCAN_ARGS(arp_tha), &n) > 0 && n > 0) {
> +            struct ovs_key_arp arp_key;
> +
> +            memset(&arp_key, 0, sizeof arp_key);
> +            arp_key.arp_sip = arp_sip;
> +            arp_key.arp_tip = arp_tip;
> +            arp_key.arp_op = htons(arp_op);
> +            memcpy(arp_key.arp_sha, arp_sha, ETH_ADDR_LEN);
> +            memcpy(arp_key.arp_tha, arp_tha, ETH_ADDR_LEN);
> +            nl_msg_put_unspec(key, OVS_KEY_ATTR_ARP, &arp_key, sizeof arp_key);
> +            return n;
> +        }
> +    }
> +
> +    {
> +        char nd_target_s[IPV6_SCAN_LEN + 1];
> +        uint8_t nd_sll[ETH_ADDR_LEN];
> +        uint8_t nd_tll[ETH_ADDR_LEN];
> +        int n = -1;
> +
> +        if (sscanf(s, "nd(target="IPV6_SCAN_FMT")%n",
> +                   nd_target_s, &n) > 0 && n > 0) {
> +            return put_nd_key(n, nd_target_s, NULL, NULL, key);
> +        }
> +        if (sscanf(s, "nd(target="IPV6_SCAN_FMT",sll="ETH_ADDR_SCAN_FMT")%n",
> +                   nd_target_s, ETH_ADDR_SCAN_ARGS(nd_sll), &n) > 0
> +            && n > 0) {
> +            return put_nd_key(n, nd_target_s, nd_sll, NULL, key);
> +        }
> +        if (sscanf(s, "nd(target="IPV6_SCAN_FMT",tll="ETH_ADDR_SCAN_FMT")%n",
> +                   nd_target_s, ETH_ADDR_SCAN_ARGS(nd_tll), &n) > 0
> +            && n > 0) {
> +            return put_nd_key(n, nd_target_s, NULL, nd_tll, key);
> +        }
> +        if (sscanf(s, "nd(target="IPV6_SCAN_FMT",sll="ETH_ADDR_SCAN_FMT","
> +                   "tll="ETH_ADDR_SCAN_FMT")%n",
> +                   nd_target_s, ETH_ADDR_SCAN_ARGS(nd_sll),
> +                   ETH_ADDR_SCAN_ARGS(nd_tll), &n) > 0
> +            && n > 0) {
> +            return put_nd_key(n, nd_target_s, nd_sll, nd_tll, key);
> +        }
> +    }
> +
> +    if (!strncmp(s, "encap(", 6)) {
> +        const char *start = s;
> +        size_t encap;
> +
> +        encap = nl_msg_start_nested(key, OVS_KEY_ATTR_ENCAP);
> +
> +        s += 6;
> +        for (;;) {
> +            int retval;
> +
> +            s += strspn(s, ", \t\r\n");
> +            if (!*s) {
> +                return -EINVAL;
> +            } else if (*s == ')') {
> +                break;
> +            }
> +
> +            retval = parse_odp_key_attr(s, port_names, key);
> +            if (retval < 0) {
> +                return retval;
> +            }
> +            s += retval;
> +        }
> +        s++;
> +
> +        nl_msg_end_nested(key, encap);
> +
> +        return s - start;
> +    }
> +
> +    return -EINVAL;
> +}
> +
> +/* Parses the string representation of a datapath flow key, in the
> + * format output by odp_flow_key_format().  Returns 0 if successful,
> + * otherwise a positive errno value.  On success, the flow key is
> + * appended to 'key' as a series of Netlink attributes.  On failure, no
> + * data is appended to 'key'.  Either way, 'key''s data might be
> + * reallocated.
> + *
> + * If 'port_names' is nonnull, it points to an simap that maps from a port name
> + * to a port number.  (Port names may be used instead of port numbers in
> + * in_port.)
> + *
> + * On success, the attributes appended to 'key' are individually syntactically
> + * valid, but they may not be valid as a sequence.  'key' might, for example,
> + * have duplicated keys.  odp_flow_key_to_flow() will detect those errors. */
> +int
> +odp_micro_flow_key_from_string(const char *s, const struct simap *port_names,
> +                               struct ofpbuf *key)
> +{
> +    const size_t old_size = key->size;
>      for (;;) {
>          int retval;
>
> @@ -1542,6 +2472,30 @@ odp_flow_key_from_string(const char *s, const struct simap *port_names,
>      return 0;
>  }
>
> +int
> +odp_mega_flow_from_string(const char *s, const struct simap *port_names,
> +                          struct ofpbuf *key, struct ofpbuf *mask)
> +{
> +    const size_t old_size = key->size;
> +    for (;;) {
> +        int retval;
> +
> +        s += strspn(s, delimiters);
> +        if (!*s) {
> +            return 0;
> +        }
> +
> +        retval = parse_odp_key_mask_attr(s, port_names, key, mask);
> +        if (retval < 0) {
> +            key->size = old_size;
> +            return -retval;
> +        }
> +        s += retval;
> +    }
> +
> +    return 0;
> +}
> +
>  static uint8_t
>  ovs_to_odp_frag(uint8_t nw_frag)
>  {
> diff --git a/lib/odp-util.h b/lib/odp-util.h
> index 6213418..37027f8 100644
> --- a/lib/odp-util.h
> +++ b/lib/odp-util.h
> @@ -90,9 +90,16 @@ struct odputil_keybuf {
>  enum odp_key_fitness odp_tun_key_from_attr(const struct nlattr *,
>                                             struct flow_tnl *);
>
> +void odp_mega_flow_format(const struct nlattr *key, size_t key_len,
> +                          const struct nlattr *mask, size_t mask_len,
> +                          struct ds *);
>  void odp_flow_key_format(const struct nlattr *, size_t, struct ds *);
> -int odp_flow_key_from_string(const char *s, const struct simap *port_names,
> -                             struct ofpbuf *);
> +int odp_micro_flow_key_from_string(const char *s,
> +                                   const struct simap *port_names,
> +                                   struct ofpbuf *);
> +int odp_mega_flow_from_string(const char *s,
> +                              const struct simap *port_names,
> +                              struct ofpbuf *, struct ofpbuf *);
>
>  void odp_flow_key_from_flow(struct ofpbuf *, const struct flow *,
>                              uint32_t odp_in_port);
> diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c
> index eb2c7e0..755a193 100644
> --- a/ofproto/ofproto-dpif.c
> +++ b/ofproto/ofproto-dpif.c
> @@ -3804,6 +3804,8 @@ handle_flow_miss_with_facet(struct flow_miss *miss, struct facet *facet,
>          put->flags = DPIF_FP_CREATE | DPIF_FP_MODIFY;
>          put->key = miss->key;
>          put->key_len = miss->key_len;
> +        put->mask = NULL;
> +        put->mask_len = 0;
>          if (want_path == SF_FAST_PATH) {
>              put->actions = facet->xout.odp_actions.data;
>              put->actions_len = facet->xout.odp_actions.size;
> @@ -4054,7 +4056,8 @@ handle_miss_upcalls(struct dpif_backer *backer, struct dpif_upcall *upcalls,
>                  hmap_insert(&backer->drop_keys, &drop_key->hmap_node,
>                              hash_bytes(drop_key->key, drop_key->key_len, 0));
>                  dpif_flow_put(backer->dpif, DPIF_FP_CREATE | DPIF_FP_MODIFY,
> -                              drop_key->key, drop_key->key_len, NULL, 0, NULL);
> +                              drop_key->key, drop_key->key_len,
> +                              NULL, 0, NULL, 0, NULL);
>              }
>              continue;
>          }
> @@ -4450,7 +4453,8 @@ update_stats(struct dpif_backer *backer)
>      size_t key_len;
>
>      dpif_flow_dump_start(&dump, backer->dpif);
> -    while (dpif_flow_dump_next(&dump, &key, &key_len, NULL, NULL, &stats)) {
> +    while (dpif_flow_dump_next(&dump, &key, &key_len,
> +                               NULL, NULL, NULL, NULL, &stats)) {
>          struct subfacet *subfacet;
>          uint32_t key_hash;
>
> @@ -5352,8 +5356,9 @@ subfacet_install(struct subfacet *subfacet, const struct ofpbuf *odp_actions,
>                            &actions, &actions_len);
>      }
>
> -    ret = dpif_flow_put(subfacet->backer->dpif, flags, subfacet->key,
> -                        subfacet->key_len, actions, actions_len, stats);
> +    ret = dpif_flow_put(ofproto->backer->dpif, flags, subfacet->key,
> +                        subfacet->key_len,  NULL, 0,
> +                        actions, actions_len, stats);
>
>      if (stats) {
>          subfacet_reset_dp_stats(subfacet, stats);
> @@ -8007,10 +8012,10 @@ ofproto_unixctl_trace(struct unixctl_conn *conn, int argc, const char *argv[],
>      }
>
>      /* Parse the flow and determine whether a datapath or
> -     * bridge is specified. If function odp_flow_key_from_string()
> +     * bridge is specified. If function odp_micro_flow_key_from_string()
>       * returns 0, the flow is a odp_flow. If function
>       * parse_ofp_exact_flow() returns 0, the flow is a br_flow. */
> -    if (!odp_flow_key_from_string(argv[argc - 1], NULL, &odp_key)) {
> +    if (!odp_micro_flow_key_from_string(argv[argc - 1], NULL, &odp_key)) {
>          /* If the odp_flow is the second argument,
>           * the datapath name is the first argument. */
>          if (argc == 3) {
> diff --git a/tests/test-odp.c b/tests/test-odp.c
> index 268a105..962d62b 100644
> --- a/tests/test-odp.c
> +++ b/tests/test-odp.c
> @@ -42,9 +42,9 @@ parse_keys(void)
>
>          /* Convert string to OVS DP key. */
>          ofpbuf_init(&odp_key, 0);
> -        error = odp_flow_key_from_string(ds_cstr(&in), NULL, &odp_key);
> +        error = odp_micro_flow_key_from_string(ds_cstr(&in), NULL, &odp_key);
>          if (error) {
> -            printf("odp_flow_key_from_string: error\n");
> +            printf("odp_micro_flow_key_from_string: error\n");
>              goto next;
>          }
>
> diff --git a/utilities/ovs-dpctl.8.in b/utilities/ovs-dpctl.8.in
> index 2b0036c..c31ddcf 100644
> --- a/utilities/ovs-dpctl.8.in
> +++ b/utilities/ovs-dpctl.8.in
> @@ -165,6 +165,14 @@ Limits \fBovs\-dpctl\fR runtime to approximately \fIsecs\fR seconds.  If
>  the timeout expires, \fBovs\-dpctl\fR will exit with a \fBSIGALRM\fR
>  signal.
>  .
> +.IP "\fB\-a\fR"
> +.IQ "\fB\-\-micro\fR"
> +Causes the add-flow, mod-flow, or del-flow commands to interpret \fIflow\fR as a micro flow. All fields needs to be fully specified with exact values.
> +.
> +.IP "\fB\-o\fR"
> +.IQ "\fB\-\-mega\fR"
> +Causes the add-flow, mod-flow, or del-flow commands to interpret \fIflow\fR as a mega flow. Flow key fields can be specified either with a value, interpreted as a exact match, or in the form of value/mask. Mask value needs be specified in Hexadecimal. Omitted key fields are interpreted as wildcarded fields. This is the default setting.
> +.
>  .so lib/vlog.man
>  .so lib/common.man
>  .
> diff --git a/utilities/ovs-dpctl.c b/utilities/ovs-dpctl.c
> index 54505e8..4e29b6d 100644
> --- a/utilities/ovs-dpctl.c
> +++ b/utilities/ovs-dpctl.c
> @@ -66,6 +66,8 @@ static bool may_create;
>   * the option itself. */
>  static int verbosity;
>
> +static bool mega_flow = true;
> +
>  static const struct command all_commands[];
>
>  static void usage(void) NO_RETURN;
> @@ -97,6 +99,8 @@ parse_options(int argc, char *argv[])
>          {"timeout", required_argument, NULL, 't'},
>          {"help", no_argument, NULL, 'h'},
>          {"version", no_argument, NULL, 'V'},
> +        {"mega", no_argument, NULL, 'a'},
> +        {"micro", no_argument, NULL, 'o'},
>          VLOG_LONG_OPTIONS,
>          {NULL, 0, NULL, 0},
>      };
> @@ -141,6 +145,14 @@ parse_options(int argc, char *argv[])
>          case 'h':
>              usage();
>
> +        case 'a':
> +            mega_flow = true;
> +            break;
> +
> +        case 'o':
> +            mega_flow = false;
> +            break;
> +
>          case 'V':
>              ovs_print_version(0, 0);
>              exit(EXIT_SUCCESS);
> @@ -185,6 +197,9 @@ usage(void)
>             "\nOptions for mod-flow:\n"
>             "  --may-create                create flow if it doesn't exist\n"
>             "  --clear                     reset existing stats to zero\n"
> +           "\nOptions for add-flow/mod-flow/del-flow:\n"
> +           "  --mega                      create a meag flow \n"
> +           "  --micro                     cerata a micro flow \n"
>             "\nOther options:\n"
>             "  -t, --timeout=SECS          give up after SECS seconds\n"
>             "  -h, --help                  display this help message\n"
> @@ -743,9 +758,11 @@ dpctl_dump_flows(int argc, char *argv[])
>      const struct nlattr *actions;
>      struct dpif_flow_dump dump;
>      const struct nlattr *key;
> +    const struct nlattr *mask;
>      size_t actions_len;
>      struct dpif *dpif;
>      size_t key_len;
> +    size_t mask_len;
>      struct ds ds;
>      char *name;
>
> @@ -756,10 +773,12 @@ dpctl_dump_flows(int argc, char *argv[])
>      ds_init(&ds);
>      dpif_flow_dump_start(&dump, dpif);
>      while (dpif_flow_dump_next(&dump, &key, &key_len,
> +                               &mask, &mask_len,
>                                 &actions, &actions_len, &stats)) {
>          ds_clear(&ds);
> -        odp_flow_key_format(key, key_len, &ds);
> +        odp_mega_flow_format(key, key_len, mask, mask_len, &ds);
>          ds_put_cstr(&ds, ", ");
> +
>          dpif_flow_stats_format(stats, &ds);
>          ds_put_cstr(&ds, ", actions:");
>          format_odp_actions(&ds, actions, actions_len);
> @@ -778,26 +797,39 @@ dpctl_put_flow(int argc, char *argv[], enum dpif_flow_put_flags flags)
>      struct dpif_flow_stats stats;
>      struct ofpbuf actions;
>      struct ofpbuf key;
> +    struct ofpbuf mask;
>      struct dpif *dpif;
> +    struct ds s;
>      char *dp_name;
>
> +    ds_init(&s);
>      ofpbuf_init(&key, 0);
> -    run(odp_flow_key_from_string(key_s, NULL, &key), "parsing flow key");
> +    ofpbuf_init(&mask, 0);
> +    if (mega_flow) {
> +        run(odp_mega_flow_from_string(key_s, NULL, &key, &mask),
> +                "parsing mega flow key");
> +    } else {
> +        run(odp_micro_flow_key_from_string(key_s, NULL, &key),
> +                "parsing micro flow key");
> +
> +    }
>
>      ofpbuf_init(&actions, 0);
>      run(odp_actions_from_string(actions_s, NULL, &actions), "parsing actions");
>
> -    dp_name = argc == 3 ? xstrdup(argv[1]) : get_one_dp();
> +    dp_name = argc == 4 ? xstrdup(argv[1]) : get_one_dp();
>      run(parsed_dpif_open(dp_name, false, &dpif), "opening datapath");
>      free(dp_name);
>
>      run(dpif_flow_put(dpif, flags,
>                        key.data, key.size,
> +                      mask.size == 0 ? NULL : mask.data, mask.size,
>                        actions.data, actions.size,
>                        print_statistics ? &stats : NULL),
>          "updating flow table");
>
>      ofpbuf_uninit(&key);
> +    ofpbuf_uninit(&mask);
>      ofpbuf_uninit(&actions);
>
>      if (print_statistics) {
> @@ -842,7 +874,7 @@ dpctl_del_flow(int argc, char *argv[])
>      char *dp_name;
>
>      ofpbuf_init(&key, 0);
> -    run(odp_flow_key_from_string(key_s, NULL, &key), "parsing flow key");
> +    run(odp_micro_flow_key_from_string(key_s, NULL, &key), "parsing flow key");
>
>      dp_name = argc == 2 ? xstrdup(argv[1]) : get_one_dp();
>      run(parsed_dpif_open(dp_name, false, &dpif), "opening datapath");
> @@ -1032,8 +1064,8 @@ dpctl_normalize_actions(int argc, char *argv[])
>
>      /* Parse flow key. */
>      ofpbuf_init(&keybuf, 0);
> -    run(odp_flow_key_from_string(argv[1], &port_names, &keybuf),
> -        "odp_flow_key_from_string");
> +    run(odp_micro_flow_key_from_string(argv[1], &port_names, &keybuf),
> +        "odp_micro_flow_key_from_string");
>
>      ds_clear(&s);
>      odp_flow_key_format(keybuf.data, keybuf.size, &s);
> --
> 1.7.9.5
>



More information about the dev mailing list