odp-util: Always encode mask of 0xffff for dl_type < ETH_TYPE_MIN.
[sliver-openvswitch.git] / lib / odp-util.c
index d70cf17..c05664c 100644 (file)
@@ -934,10 +934,9 @@ format_odp_key_attr(const struct nlattr *a, const struct nlattr *ma,
     enum ovs_key_attr attr = nl_attr_type(a);
     char namebuf[OVS_KEY_ATTR_BUFSIZE];
     int expected_len;
+    bool is_exact;
 
-    if (ma && odp_mask_attr_is_exact(ma)) {
-        ma = NULL;
-    }
+    is_exact = ma ? odp_mask_attr_is_exact(ma) : true;
 
     ds_put_cstr(ds, ovs_key_attr_to_string(attr, namebuf, sizeof namebuf));
 
@@ -981,7 +980,7 @@ format_odp_key_attr(const struct nlattr *a, const struct nlattr *ma,
     case OVS_KEY_ATTR_PRIORITY:
     case OVS_KEY_ATTR_SKB_MARK:
         ds_put_format(ds, "%#"PRIx32, nl_attr_get_u32(a));
-        if (ma) {
+        if (!is_exact) {
             ds_put_format(ds, "/%#"PRIx32, nl_attr_get_u32(ma));
         }
         break;
@@ -990,7 +989,7 @@ format_odp_key_attr(const struct nlattr *a, const struct nlattr *ma,
         memset(&tun_key, 0, sizeof tun_key);
         if (odp_tun_key_from_attr(a, &tun_key) == ODP_FIT_ERROR) {
             ds_put_format(ds, "error");
-        } else if (ma) {
+        } else if (!is_exact) {
             struct flow_tnl tun_mask;
 
             memset(&tun_mask, 0, sizeof tun_mask);
@@ -1029,13 +1028,13 @@ format_odp_key_attr(const struct nlattr *a, const struct nlattr *ma,
 
     case OVS_KEY_ATTR_IN_PORT:
         ds_put_format(ds, "%"PRIu32, nl_attr_get_u32(a));
-        if (ma) {
+        if (!is_exact) {
             ds_put_format(ds, "/%#"PRIx32, nl_attr_get_u32(ma));
         }
         break;
 
     case OVS_KEY_ATTR_ETHERNET:
-        if (ma) {
+        if (!is_exact) {
             const struct ovs_key_ethernet *eth_mask = nl_attr_get(ma);
             const struct ovs_key_ethernet *eth_key = nl_attr_get(a);
 
@@ -1057,13 +1056,13 @@ format_odp_key_attr(const struct nlattr *a, const struct nlattr *ma,
     case OVS_KEY_ATTR_VLAN:
         {
             ovs_be16 vlan_tci = nl_attr_get_be16(a);
-            if (ma) {
+            if (!is_exact) {
                 ovs_be16 mask = nl_attr_get_be16(ma);
                 ds_put_format(ds, "vid=%"PRIu16"/%"PRIx16",pcp=%d/0x%x,cfi=%d/%d",
                               vlan_tci_to_vid(vlan_tci),
                               vlan_tci_to_vid(mask),
                               vlan_tci_to_pcp(vlan_tci),
-                              vlan_tci_to_vid(mask),
+                              vlan_tci_to_pcp(mask),
                               vlan_tci_to_cfi(vlan_tci),
                               vlan_tci_to_cfi(mask));
             } else {
@@ -1075,7 +1074,7 @@ format_odp_key_attr(const struct nlattr *a, const struct nlattr *ma,
     case OVS_KEY_ATTR_MPLS: {
         const struct ovs_key_mpls *mpls_key = nl_attr_get(a);
         const struct ovs_key_mpls *mpls_mask = NULL;
-        if (ma) {
+        if (!is_exact) {
             mpls_mask = nl_attr_get(ma);
         }
         format_mpls(ds, mpls_key, mpls_mask);
@@ -1084,13 +1083,13 @@ format_odp_key_attr(const struct nlattr *a, const struct nlattr *ma,
 
     case OVS_KEY_ATTR_ETHERTYPE:
         ds_put_format(ds, "0x%04"PRIx16, ntohs(nl_attr_get_be16(a)));
-        if (ma) {
+        if (!is_exact) {
             ds_put_format(ds, "/0x%04"PRIx16, ntohs(nl_attr_get_be16(ma)));
         }
         break;
 
     case OVS_KEY_ATTR_IPV4:
-        if (ma) {
+        if (!is_exact) {
             const struct ovs_key_ipv4 *ipv4_key = nl_attr_get(a);
             const struct ovs_key_ipv4 *ipv4_mask = nl_attr_get(ma);
 
@@ -1120,7 +1119,7 @@ format_odp_key_attr(const struct nlattr *a, const struct nlattr *ma,
         break;
 
     case OVS_KEY_ATTR_IPV6:
-        if (ma) {
+        if (!is_exact) {
             const struct ovs_key_ipv6 *ipv6_key, *ipv6_mask;
             char src_str[INET6_ADDRSTRLEN];
             char dst_str[INET6_ADDRSTRLEN];
@@ -1165,7 +1164,7 @@ format_odp_key_attr(const struct nlattr *a, const struct nlattr *ma,
         break;
 
     case OVS_KEY_ATTR_TCP:
-        if (ma) {
+        if (!is_exact) {
             const struct ovs_key_tcp *tcp_mask = nl_attr_get(ma);
             const struct ovs_key_tcp *tcp_key = nl_attr_get(a);
 
@@ -1182,7 +1181,7 @@ format_odp_key_attr(const struct nlattr *a, const struct nlattr *ma,
         break;
 
     case OVS_KEY_ATTR_UDP:
-        if (ma) {
+        if (!is_exact) {
             const struct ovs_key_udp *udp_mask = nl_attr_get(ma);
             const struct ovs_key_udp *udp_key = nl_attr_get(a);
 
@@ -1199,7 +1198,7 @@ format_odp_key_attr(const struct nlattr *a, const struct nlattr *ma,
         break;
 
     case OVS_KEY_ATTR_ICMP:
-        if (ma) {
+        if (!is_exact) {
             const struct ovs_key_icmp *icmp_mask = nl_attr_get(ma);
             const struct ovs_key_icmp *icmp_key = nl_attr_get(a);
 
@@ -1215,7 +1214,7 @@ format_odp_key_attr(const struct nlattr *a, const struct nlattr *ma,
         break;
 
     case OVS_KEY_ATTR_ICMPV6:
-        if (ma) {
+        if (!is_exact) {
             const struct ovs_key_icmpv6 *icmpv6_mask = nl_attr_get(ma);
             const struct ovs_key_icmpv6 *icmpv6_key = nl_attr_get(a);
 
@@ -1231,7 +1230,7 @@ format_odp_key_attr(const struct nlattr *a, const struct nlattr *ma,
         break;
 
     case OVS_KEY_ATTR_ARP:
-        if (ma) {
+        if (!is_exact) {
             const struct ovs_key_arp *arp_mask = nl_attr_get(ma);
             const struct ovs_key_arp *arp_key = nl_attr_get(a);
 
@@ -1261,15 +1260,17 @@ format_odp_key_attr(const struct nlattr *a, const struct nlattr *ma,
         break;
 
     case OVS_KEY_ATTR_ND: {
-        const struct ovs_key_nd *nd_key, *nd_mask;
+        const struct ovs_key_nd *nd_key, *nd_mask = NULL;
         char target[INET6_ADDRSTRLEN];
 
         nd_key = nl_attr_get(a);
-        nd_mask = ma ? nl_attr_get(ma) : NULL;
+        if (!is_exact) {
+            nd_mask = nl_attr_get(ma);
+        }
 
         inet_ntop(AF_INET6, nd_key->nd_target, target, sizeof target);
         ds_put_format(ds, "target=%s", target);
-        if (nd_mask) {
+        if (!is_exact) {
             inet_ntop(AF_INET6, nd_mask->nd_target, target, sizeof target);
             ds_put_format(ds, "/%s", target);
         }
@@ -1277,7 +1278,7 @@ format_odp_key_attr(const struct nlattr *a, const struct nlattr *ma,
         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) {
+            if (!is_exact) {
                 ds_put_format(ds, "/"ETH_ADDR_FMT,
                               ETH_ADDR_ARGS(nd_mask->nd_sll));
             }
@@ -1285,7 +1286,7 @@ format_odp_key_attr(const struct nlattr *a, const struct nlattr *ma,
         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) {
+            if (!is_exact) {
                 ds_put_format(ds, "/"ETH_ADDR_FMT,
                               ETH_ADDR_ARGS(nd_mask->nd_tll));
             }
@@ -1297,7 +1298,7 @@ format_odp_key_attr(const struct nlattr *a, const struct nlattr *ma,
     case __OVS_KEY_ATTR_MAX:
     default:
         format_generic_odp_key(a, ds);
-        if (ma) {
+        if (!is_exact) {
             ds_put_char(ds, '/');
             format_generic_odp_key(ma, ds);
         }
@@ -1306,6 +1307,29 @@ format_odp_key_attr(const struct nlattr *a, const struct nlattr *ma,
     ds_put_char(ds, ')');
 }
 
+static struct nlattr *
+generate_all_wildcard_mask(struct ofpbuf *ofp, const struct nlattr *key)
+{
+    const struct nlattr *a;
+    unsigned int left;
+    int type = nl_attr_type(key);
+    int size = nl_attr_get_size(key);
+
+    if (odp_flow_key_attr_len(type) >=0) {
+        memset(nl_msg_put_unspec_uninit(ofp, type, size), 0, size);
+    } else {
+        size_t nested_mask;
+
+        nested_mask = nl_msg_start_nested(ofp, type);
+        NL_ATTR_FOR_EACH(a, left, key, nl_attr_get_size(key)) {
+            generate_all_wildcard_mask(ofp, nl_attr_get(a));
+        }
+        nl_msg_end_nested(ofp, nested_mask);
+    }
+
+    return ofp->base;
+}
+
 /* Appends to 'ds' a string representation of the 'key_len' bytes of
  * OVS_KEY_ATTR_* attributes in 'key'. If non-null, additionally formats the
  * 'mask_len' bytes of 'mask' which apply to 'key'. */
@@ -1319,7 +1343,9 @@ odp_flow_format(const struct nlattr *key, size_t key_len,
         unsigned int left;
         bool has_ethtype_key = false;
         const struct nlattr *ma = NULL;
+        struct ofpbuf ofp;
 
+        ofpbuf_init(&ofp, 100);
         NL_ATTR_FOR_EACH (a, left, key, key_len) {
             if (a != key) {
                 ds_put_char(ds, ',');
@@ -1329,9 +1355,15 @@ odp_flow_format(const struct nlattr *key, size_t key_len,
             }
             if (mask && mask_len) {
                 ma = nl_attr_find__(mask, mask_len, nl_attr_type(a));
+                if (!ma) {
+                    ma = generate_all_wildcard_mask(&ofp, a);
+                }
             }
             format_odp_key_attr(a, ma, ds);
+            ofpbuf_clear(&ofp);
         }
+        ofpbuf_uninit(&ofp);
+
         if (left) {
             int i;
             
@@ -1365,23 +1397,51 @@ odp_flow_key_format(const struct nlattr *key,
     odp_flow_format(key, key_len, NULL, 0, ds);
 }
 
+static void
+put_nd(struct ovs_key_nd* nd_key, const uint8_t *nd_sll,
+       const uint8_t *nd_tll, struct ofpbuf *key)
+{
+    if (nd_sll) {
+        memcpy(nd_key->nd_sll, nd_sll, ETH_ADDR_LEN);
+    }
+
+    if (nd_tll) {
+        memcpy(nd_key->nd_tll, nd_tll, ETH_ADDR_LEN);
+    }
+
+    nl_msg_put_unspec(key, OVS_KEY_ATTR_ND, nd_key, sizeof *nd_key);
+}
+
 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)
+put_nd_key(int n, const char *nd_target_s, const uint8_t *nd_sll,
+           const uint8_t *nd_tll, struct ofpbuf *key)
 {
     struct ovs_key_nd nd_key;
 
     memset(&nd_key, 0, sizeof nd_key);
+
     if (inet_pton(AF_INET6, nd_target_s, nd_key.nd_target) != 1) {
         return -EINVAL;
     }
-    if (nd_sll) {
-        memcpy(nd_key.nd_sll, nd_sll, ETH_ADDR_LEN);
-    }
-    if (nd_tll) {
-        memcpy(nd_key.nd_tll, nd_tll, ETH_ADDR_LEN);
+
+    put_nd(&nd_key, nd_sll, nd_tll, key);
+    return n;
+}
+
+static int
+put_nd_mask(int n, const char *nd_target_s,
+           const uint8_t *nd_sll, const uint8_t *nd_tll, struct ofpbuf *mask)
+{
+    struct ovs_key_nd nd_mask;
+
+    memset(&nd_mask, 0xff, sizeof nd_mask);
+
+    if (strlen(nd_target_s) != 0 &&
+            inet_pton(AF_INET6, nd_target_s, nd_mask.nd_target) != 1) {
+        return -EINVAL;
     }
-    nl_msg_put_unspec(key, OVS_KEY_ATTR_ND, &nd_key, sizeof nd_key);
+
+    put_nd(&nd_mask, nd_sll, nd_tll, mask);
     return n;
 }
 
@@ -2096,19 +2156,19 @@ parse_odp_key_mask_attr(const char *s, const struct simap *port_names,
         uint8_t nd_tll_mask[ETH_ADDR_LEN];
         int n = -1;
 
-        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);
+        nd_target_mask_s[0] = 0;
+        memset(nd_sll_mask, 0xff, sizeof nd_sll_mask);
+        memset(nd_tll_mask, 0xff, sizeof nd_tll_mask);
 
         if (mask && 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);
+                put_nd_mask(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) {
                 put_nd_key(n, nd_target_s, NULL, NULL, key);
                 if (mask) {
-                    put_nd_key(n, nd_target_mask_s, NULL, NULL, mask);
+                    put_nd_mask(n, nd_target_mask_s, NULL, NULL, mask);
                 }
         } else if (mask && sscanf(s, "nd(target="IPV6_SCAN_FMT"/"IPV6_SCAN_FMT
                          ",sll="ETH_ADDR_SCAN_FMT"/"ETH_ADDR_SCAN_FMT")%n",
@@ -2116,13 +2176,13 @@ parse_odp_key_mask_attr(const char *s, const struct simap *port_names,
                    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);
+            put_nd_mask(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) {
             put_nd_key(n, nd_target_s, nd_sll, NULL, key);
             if (mask) {
-                put_nd_key(n, nd_target_mask_s, nd_sll_mask, NULL, mask);
+                put_nd_mask(n, nd_target_mask_s, nd_sll_mask, NULL, mask);
             }
         } else if (mask && sscanf(s, "nd(target="IPV6_SCAN_FMT"/"IPV6_SCAN_FMT
                          ",tll="ETH_ADDR_SCAN_FMT"/"ETH_ADDR_SCAN_FMT")%n",
@@ -2130,13 +2190,13 @@ parse_odp_key_mask_attr(const char *s, const struct simap *port_names,
                    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);
+            put_nd_mask(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) {
             put_nd_key(n, nd_target_s, NULL, nd_tll, key);
             if (mask) {
-                put_nd_key(n, nd_target_mask_s, NULL, nd_tll_mask, mask);
+                put_nd_mask(n, nd_target_mask_s, NULL, nd_tll_mask, mask);
             }
         } else if (mask && sscanf(s, "nd(target="IPV6_SCAN_FMT"/"IPV6_SCAN_FMT
                    ",sll="ETH_ADDR_SCAN_FMT"/"ETH_ADDR_SCAN_FMT","
@@ -2147,7 +2207,7 @@ parse_odp_key_mask_attr(const char *s, const struct simap *port_names,
                    &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);
+            put_nd_mask(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),
@@ -2155,7 +2215,8 @@ parse_odp_key_mask_attr(const char *s, const struct simap *port_names,
             && n > 0) {
             put_nd_key(n, nd_target_s, nd_sll, nd_tll, key);
             if (mask) {
-                put_nd_key(n, nd_target_mask_s, nd_sll_mask, nd_tll_mask, mask);
+                put_nd_mask(n, nd_target_mask_s,
+                            nd_sll_mask, nd_tll_mask, mask);
             }
         }
 
@@ -2249,45 +2310,48 @@ ovs_to_odp_frag(uint8_t nw_frag)
           : OVS_FRAG_TYPE_LATER);
 }
 
-/* Appends a representation of 'flow' as OVS_KEY_ATTR_* attributes to 'buf'.
- * 'flow->in_port' is ignored (since it is likely to be an OpenFlow port
- * number rather than a datapath port number).  Instead, if 'odp_in_port'
- * is anything other than ODPP_NONE, it is included in 'buf' as the input
- * port.
- *
- * 'buf' must have at least ODPUTIL_FLOW_KEY_BYTES bytes of space, or be
- * capable of being expanded to allow for that much space. */
-void
-odp_flow_key_from_flow(struct ofpbuf *buf, const struct flow *flow,
-                       odp_port_t odp_in_port)
+static void
+odp_flow_key_from_flow__(struct ofpbuf *buf, const struct flow *data,
+                         const struct flow *flow, odp_port_t odp_in_port)
 {
+    bool is_mask;
     struct ovs_key_ethernet *eth_key;
     size_t encap;
 
+    /* We assume that if 'data' and 'flow' are not the same, we should
+     * treat 'data' as a mask. */
+    is_mask = (data != flow);
+
     if (flow->skb_priority) {
-        nl_msg_put_u32(buf, OVS_KEY_ATTR_PRIORITY, flow->skb_priority);
+        nl_msg_put_u32(buf, OVS_KEY_ATTR_PRIORITY, data->skb_priority);
     }
 
     if (flow->tunnel.ip_dst) {
-        tun_key_to_attr(buf, &flow->tunnel);
+        tun_key_to_attr(buf, &data->tunnel);
     }
 
     if (flow->skb_mark) {
-        nl_msg_put_u32(buf, OVS_KEY_ATTR_SKB_MARK, flow->skb_mark);
+        nl_msg_put_u32(buf, OVS_KEY_ATTR_SKB_MARK, data->skb_mark);
     }
 
-    if (odp_in_port != ODPP_NONE) {
+    /* Add an ingress port attribute if this is a mask or 'odp_in_port'
+     * is not the magical value "ODPP_NONE". */
+    if (is_mask || odp_in_port != ODPP_NONE) {
         nl_msg_put_odp_port(buf, OVS_KEY_ATTR_IN_PORT, odp_in_port);
     }
 
     eth_key = nl_msg_put_unspec_uninit(buf, OVS_KEY_ATTR_ETHERNET,
                                        sizeof *eth_key);
-    memcpy(eth_key->eth_src, flow->dl_src, ETH_ADDR_LEN);
-    memcpy(eth_key->eth_dst, flow->dl_dst, ETH_ADDR_LEN);
+    memcpy(eth_key->eth_src, data->dl_src, ETH_ADDR_LEN);
+    memcpy(eth_key->eth_dst, data->dl_dst, ETH_ADDR_LEN);
 
     if (flow->vlan_tci != htons(0) || flow->dl_type == htons(ETH_TYPE_VLAN)) {
-        nl_msg_put_be16(buf, OVS_KEY_ATTR_ETHERTYPE, htons(ETH_TYPE_VLAN));
-        nl_msg_put_be16(buf, OVS_KEY_ATTR_VLAN, flow->vlan_tci);
+        if (is_mask) {
+            nl_msg_put_be16(buf, OVS_KEY_ATTR_ETHERTYPE, htons(UINT16_MAX));
+        } else {
+            nl_msg_put_be16(buf, OVS_KEY_ATTR_ETHERTYPE, htons(ETH_TYPE_VLAN));
+        }
+        nl_msg_put_be16(buf, OVS_KEY_ATTR_VLAN, data->vlan_tci);
         encap = nl_msg_start_nested(buf, OVS_KEY_ATTR_ENCAP);
         if (flow->vlan_tci == htons(0)) {
             goto unencap;
@@ -2297,33 +2361,47 @@ odp_flow_key_from_flow(struct ofpbuf *buf, const struct flow *flow,
     }
 
     if (ntohs(flow->dl_type) < ETH_TYPE_MIN) {
+        /* For backwards compatibility with kernels that don't support
+         * wildcarding, the following convention is used to encode the
+         * OVS_KEY_ATTR_ETHERTYPE for key and mask:
+         *
+         *   key      mask    matches
+         * -------- --------  -------
+         *  >0x5ff   0xffff   Specified Ethernet II Ethertype.
+         *  >0x5ff      0     Any Ethernet II or non-Ethernet II frame.
+         *  <none>   0xffff   Any non-Ethernet II frame (except valid
+         *                    802.3 SNAP packet with valid eth_type).
+         */
+        if (is_mask) {
+            nl_msg_put_be16(buf, OVS_KEY_ATTR_ETHERTYPE, htons(UINT16_MAX));
+        }
         goto unencap;
     }
 
-    nl_msg_put_be16(buf, OVS_KEY_ATTR_ETHERTYPE, flow->dl_type);
+    nl_msg_put_be16(buf, OVS_KEY_ATTR_ETHERTYPE, data->dl_type);
 
     if (flow->dl_type == htons(ETH_TYPE_IP)) {
         struct ovs_key_ipv4 *ipv4_key;
 
         ipv4_key = nl_msg_put_unspec_uninit(buf, OVS_KEY_ATTR_IPV4,
                                             sizeof *ipv4_key);
-        ipv4_key->ipv4_src = flow->nw_src;
-        ipv4_key->ipv4_dst = flow->nw_dst;
-        ipv4_key->ipv4_proto = flow->nw_proto;
-        ipv4_key->ipv4_tos = flow->nw_tos;
-        ipv4_key->ipv4_ttl = flow->nw_ttl;
-        ipv4_key->ipv4_frag = ovs_to_odp_frag(flow->nw_frag);
+        ipv4_key->ipv4_src = data->nw_src;
+        ipv4_key->ipv4_dst = data->nw_dst;
+        ipv4_key->ipv4_proto = data->nw_proto;
+        ipv4_key->ipv4_tos = data->nw_tos;
+        ipv4_key->ipv4_ttl = data->nw_ttl;
+        ipv4_key->ipv4_frag = ovs_to_odp_frag(data->nw_frag);
     } else if (flow->dl_type == htons(ETH_TYPE_IPV6)) {
         struct ovs_key_ipv6 *ipv6_key;
 
         ipv6_key = nl_msg_put_unspec_uninit(buf, OVS_KEY_ATTR_IPV6,
                                             sizeof *ipv6_key);
-        memcpy(ipv6_key->ipv6_src, &flow->ipv6_src, sizeof ipv6_key->ipv6_src);
-        memcpy(ipv6_key->ipv6_dst, &flow->ipv6_dst, sizeof ipv6_key->ipv6_dst);
-        ipv6_key->ipv6_label = flow->ipv6_label;
-        ipv6_key->ipv6_proto = flow->nw_proto;
-        ipv6_key->ipv6_tclass = flow->nw_tos;
-        ipv6_key->ipv6_hlimit = flow->nw_ttl;
+        memcpy(ipv6_key->ipv6_src, &data->ipv6_src, sizeof ipv6_key->ipv6_src);
+        memcpy(ipv6_key->ipv6_dst, &data->ipv6_dst, sizeof ipv6_key->ipv6_dst);
+        ipv6_key->ipv6_label = data->ipv6_label;
+        ipv6_key->ipv6_proto = data->nw_proto;
+        ipv6_key->ipv6_tclass = data->nw_tos;
+        ipv6_key->ipv6_hlimit = data->nw_ttl;
         ipv6_key->ipv6_frag = ovs_to_odp_frag(flow->nw_frag);
     } else if (flow->dl_type == htons(ETH_TYPE_ARP) ||
                flow->dl_type == htons(ETH_TYPE_RARP)) {
@@ -2332,11 +2410,11 @@ odp_flow_key_from_flow(struct ofpbuf *buf, const struct flow *flow,
         arp_key = nl_msg_put_unspec_uninit(buf, OVS_KEY_ATTR_ARP,
                                            sizeof *arp_key);
         memset(arp_key, 0, sizeof *arp_key);
-        arp_key->arp_sip = flow->nw_src;
-        arp_key->arp_tip = flow->nw_dst;
-        arp_key->arp_op = htons(flow->nw_proto);
-        memcpy(arp_key->arp_sha, flow->arp_sha, ETH_ADDR_LEN);
-        memcpy(arp_key->arp_tha, flow->arp_tha, ETH_ADDR_LEN);
+        arp_key->arp_sip = data->nw_src;
+        arp_key->arp_tip = data->nw_dst;
+        arp_key->arp_op = htons(data->nw_proto);
+        memcpy(arp_key->arp_sha, data->arp_sha, ETH_ADDR_LEN);
+        memcpy(arp_key->arp_tha, data->arp_tha, ETH_ADDR_LEN);
     }
 
     if (flow->mpls_depth) {
@@ -2344,7 +2422,7 @@ odp_flow_key_from_flow(struct ofpbuf *buf, const struct flow *flow,
 
         mpls_key = nl_msg_put_unspec_uninit(buf, OVS_KEY_ATTR_MPLS,
                                             sizeof *mpls_key);
-        mpls_key->mpls_lse = flow->mpls_lse;
+        mpls_key->mpls_lse = data->mpls_lse;
     }
 
     if (is_ip_any(flow) && !(flow->nw_frag & FLOW_NW_FRAG_LATER)) {
@@ -2353,31 +2431,31 @@ odp_flow_key_from_flow(struct ofpbuf *buf, const struct flow *flow,
 
             tcp_key = nl_msg_put_unspec_uninit(buf, OVS_KEY_ATTR_TCP,
                                                sizeof *tcp_key);
-            tcp_key->tcp_src = flow->tp_src;
-            tcp_key->tcp_dst = flow->tp_dst;
+            tcp_key->tcp_src = data->tp_src;
+            tcp_key->tcp_dst = data->tp_dst;
         } else if (flow->nw_proto == IPPROTO_UDP) {
             struct ovs_key_udp *udp_key;
 
             udp_key = nl_msg_put_unspec_uninit(buf, OVS_KEY_ATTR_UDP,
                                                sizeof *udp_key);
-            udp_key->udp_src = flow->tp_src;
-            udp_key->udp_dst = flow->tp_dst;
+            udp_key->udp_src = data->tp_src;
+            udp_key->udp_dst = data->tp_dst;
         } else if (flow->dl_type == htons(ETH_TYPE_IP)
                 && flow->nw_proto == IPPROTO_ICMP) {
             struct ovs_key_icmp *icmp_key;
 
             icmp_key = nl_msg_put_unspec_uninit(buf, OVS_KEY_ATTR_ICMP,
                                                 sizeof *icmp_key);
-            icmp_key->icmp_type = ntohs(flow->tp_src);
-            icmp_key->icmp_code = ntohs(flow->tp_dst);
+            icmp_key->icmp_type = ntohs(data->tp_src);
+            icmp_key->icmp_code = ntohs(data->tp_dst);
         } else if (flow->dl_type == htons(ETH_TYPE_IPV6)
                 && flow->nw_proto == IPPROTO_ICMPV6) {
             struct ovs_key_icmpv6 *icmpv6_key;
 
             icmpv6_key = nl_msg_put_unspec_uninit(buf, OVS_KEY_ATTR_ICMPV6,
                                                   sizeof *icmpv6_key);
-            icmpv6_key->icmpv6_type = ntohs(flow->tp_src);
-            icmpv6_key->icmpv6_code = ntohs(flow->tp_dst);
+            icmpv6_key->icmpv6_type = ntohs(data->tp_src);
+            icmpv6_key->icmpv6_code = ntohs(data->tp_dst);
 
             if (icmpv6_key->icmpv6_type == ND_NEIGHBOR_SOLICIT
                     || icmpv6_key->icmpv6_type == ND_NEIGHBOR_ADVERT) {
@@ -2385,10 +2463,10 @@ odp_flow_key_from_flow(struct ofpbuf *buf, const struct flow *flow,
 
                 nd_key = nl_msg_put_unspec_uninit(buf, OVS_KEY_ATTR_ND,
                                                     sizeof *nd_key);
-                memcpy(nd_key->nd_target, &flow->nd_target,
+                memcpy(nd_key->nd_target, &data->nd_target,
                         sizeof nd_key->nd_target);
-                memcpy(nd_key->nd_sll, flow->arp_sha, ETH_ADDR_LEN);
-                memcpy(nd_key->nd_tll, flow->arp_tha, ETH_ADDR_LEN);
+                memcpy(nd_key->nd_sll, data->arp_sha, ETH_ADDR_LEN);
+                memcpy(nd_key->nd_tll, data->arp_tha, ETH_ADDR_LEN);
             }
         }
     }
@@ -2399,6 +2477,36 @@ unencap:
     }
 }
 
+/* Appends a representation of 'flow' as OVS_KEY_ATTR_* attributes to 'buf'.
+ * 'flow->in_port' is ignored (since it is likely to be an OpenFlow port
+ * number rather than a datapath port number).  Instead, if 'odp_in_port'
+ * is anything other than ODPP_NONE, it is included in 'buf' as the input
+ * port.
+ *
+ * 'buf' must have at least ODPUTIL_FLOW_KEY_BYTES bytes of space, or be
+ * capable of being expanded to allow for that much space. */
+void
+odp_flow_key_from_flow(struct ofpbuf *buf, const struct flow *flow,
+                       odp_port_t odp_in_port)
+{
+    odp_flow_key_from_flow__(buf, flow, flow, odp_in_port);
+}
+
+/* Appends a representation of 'mask' as OVS_KEY_ATTR_* attributes to
+ * 'buf'.  'flow' is used as a template to determine how to interpret
+ * 'mask'.  For example, the 'dl_type' of 'mask' describes the mask, but
+ * it doesn't indicate whether the other fields should be interpreted as
+ * ARP, IPv4, IPv6, etc.
+ *
+ * 'buf' must have at least ODPUTIL_FLOW_KEY_BYTES bytes of space, or be
+ * capable of being expanded to allow for that much space. */
+void
+odp_flow_key_from_mask(struct ofpbuf *buf, const struct flow *mask,
+                       const struct flow *flow, uint32_t odp_in_port_mask)
+{
+    odp_flow_key_from_flow__(buf, mask, flow, u32_to_odp(odp_in_port_mask));
+}
+
 uint32_t
 odp_flow_key_hash(const struct nlattr *key, size_t key_len)
 {