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

Andy Zhou azhou at nicira.com
Tue Jun 11 23:45:10 UTC 2013


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