ofproto-dpif: Tighten up megaflow wildcard handling.
[sliver-openvswitch.git] / lib / odp-util.c
index 80acad3..c4127ea 100644 (file)
@@ -86,11 +86,13 @@ odp_action_len(uint16_t type)
     return -1;
 }
 
+/* Returns a string form of 'attr'.  The return value is either a statically
+ * allocated constant string or the 'bufsize'-byte buffer 'namebuf'.  'bufsize'
+ * should be at least OVS_KEY_ATTR_BUFSIZE. */
+enum { OVS_KEY_ATTR_BUFSIZE = 3 + INT_STRLEN(unsigned int) + 1 };
 static const char *
-ovs_key_attr_to_string(enum ovs_key_attr attr)
+ovs_key_attr_to_string(enum ovs_key_attr attr, char *namebuf, size_t bufsize)
 {
-    static char unknown_attr[3 + INT_STRLEN(unsigned int) + 1];
-
     switch (attr) {
     case OVS_KEY_ATTR_UNSPEC: return "unspec";
     case OVS_KEY_ATTR_ENCAP: return "encap";
@@ -113,9 +115,8 @@ ovs_key_attr_to_string(enum ovs_key_attr attr)
 
     case __OVS_KEY_ATTR_MAX:
     default:
-        snprintf(unknown_attr, sizeof unknown_attr, "key%u",
-                 (unsigned int) attr);
-        return unknown_attr;
+        snprintf(namebuf, bufsize, "key%u", (unsigned int) attr);
+        return namebuf;
     }
 }
 
@@ -170,28 +171,39 @@ format_odp_sample_action(struct ds *ds, const struct nlattr *attr)
 }
 
 static const char *
-slow_path_reason_to_string(uint32_t data)
+slow_path_reason_to_string(enum slow_path_reason reason)
 {
-    enum slow_path_reason bit = (enum slow_path_reason) data;
-
-    switch (bit) {
+    switch (reason) {
     case SLOW_CFM:
         return "cfm";
     case SLOW_LACP:
         return "lacp";
     case SLOW_STP:
         return "stp";
-    case SLOW_IN_BAND:
-        return "in_band";
+    case SLOW_BFD:
+        return "bfd";
     case SLOW_CONTROLLER:
         return "controller";
-    case SLOW_MATCH:
-        return "match";
+    case __SLOW_MAX:
     default:
         return NULL;
     }
 }
 
+static enum slow_path_reason
+string_to_slow_path_reason(const char *string)
+{
+    enum slow_path_reason i;
+
+    for (i = 1; i < __SLOW_MAX; i++) {
+        if (!strcmp(string, slow_path_reason_to_string(i))) {
+            return i;
+        }
+    }
+
+    return 0;
+}
+
 static int
 parse_flags(const char *s, const char *(*bit_to_string)(uint32_t),
             uint32_t *res)
@@ -262,43 +274,60 @@ format_odp_userspace_action(struct ds *ds, const struct nlattr *attr)
                   nl_attr_get_u32(a[OVS_USERSPACE_ATTR_PID]));
 
     userdata_attr = a[OVS_USERSPACE_ATTR_USERDATA];
-    if (userdata_attr && nl_attr_get_size(userdata_attr) == sizeof(uint64_t)) {
-        uint64_t userdata = nl_attr_get_u64(a[OVS_USERSPACE_ATTR_USERDATA]);
-        union user_action_cookie cookie;
-
-        memcpy(&cookie, &userdata, sizeof cookie);
-
-        switch (cookie.type) {
-        case USER_ACTION_COOKIE_SFLOW:
-            ds_put_format(ds, ",sFlow("
-                          "vid=%"PRIu16",pcp=%"PRIu8",output=%"PRIu32")",
-                          vlan_tci_to_vid(cookie.sflow.vlan_tci),
-                          vlan_tci_to_pcp(cookie.sflow.vlan_tci),
-                          cookie.sflow.output);
-            break;
 
-        case USER_ACTION_COOKIE_SLOW_PATH:
-            ds_put_cstr(ds, ",slow_path(");
-            format_flags(ds, slow_path_reason_to_string,
-                         cookie.slow_path.reason, ',');
-            ds_put_format(ds, ")");
-            break;
+    if (userdata_attr) {
+        const uint8_t *userdata = nl_attr_get(userdata_attr);
+        size_t userdata_len = nl_attr_get_size(userdata_attr);
+        bool userdata_unspec = true;
+        union user_action_cookie cookie;
 
-        case USER_ACTION_COOKIE_UNSPEC:
-        default:
-            ds_put_format(ds, ",userdata=0x%"PRIx64, userdata);
-            break;
+        if (userdata_len >= sizeof cookie.type
+            && userdata_len <= sizeof cookie) {
+
+            memset(&cookie, 0, sizeof cookie);
+            memcpy(&cookie, userdata, userdata_len);
+
+            userdata_unspec = false;
+
+            if (userdata_len == sizeof cookie.sflow
+                && cookie.type == USER_ACTION_COOKIE_SFLOW) {
+                ds_put_format(ds, ",sFlow("
+                              "vid=%"PRIu16",pcp=%"PRIu8",output=%"PRIu32")",
+                              vlan_tci_to_vid(cookie.sflow.vlan_tci),
+                              vlan_tci_to_pcp(cookie.sflow.vlan_tci),
+                              cookie.sflow.output);
+            } else if (userdata_len == sizeof cookie.slow_path
+                       && cookie.type == USER_ACTION_COOKIE_SLOW_PATH) {
+                const char *reason;
+                reason = slow_path_reason_to_string(cookie.slow_path.reason);
+                reason = reason ? reason : "";
+                ds_put_format(ds, ",slow_path(%s)", reason);
+            } else if (userdata_len == sizeof cookie.flow_sample
+                       && cookie.type == USER_ACTION_COOKIE_FLOW_SAMPLE) {
+                ds_put_format(ds, ",flow_sample(probability=%"PRIu16
+                              ",collector_set_id=%"PRIu32
+                              ",obs_domain_id=%"PRIu32
+                              ",obs_point_id=%"PRIu32")",
+                              cookie.flow_sample.probability,
+                              cookie.flow_sample.collector_set_id,
+                              cookie.flow_sample.obs_domain_id,
+                              cookie.flow_sample.obs_point_id);
+            } else if (userdata_len == sizeof cookie.ipfix
+                       && cookie.type == USER_ACTION_COOKIE_IPFIX) {
+                ds_put_format(ds, ",ipfix");
+            } else {
+                userdata_unspec = true;
+            }
         }
-    } else if (userdata_attr) {
-        const uint8_t *userdata = nl_attr_get(userdata_attr);
-        size_t len = nl_attr_get_size(userdata_attr);
-        size_t i;
 
-        ds_put_format(ds, ",userdata(");
-        for (i = 0; i < len; i++) {
-            ds_put_format(ds, "%02x", userdata[i]);
+        if (userdata_unspec) {
+            size_t i;
+            ds_put_format(ds, ",userdata(");
+            for (i = 0; i < userdata_len; i++) {
+                ds_put_format(ds, "%02x", userdata[i]);
+            }
+            ds_put_char(ds, ')');
         }
-        ds_put_char(ds, ')');
     }
 
     ds_put_char(ds, ')');
@@ -342,7 +371,7 @@ format_odp_action(struct ds *ds, const struct nlattr *a)
 
     switch (type) {
     case OVS_ACTION_ATTR_OUTPUT:
-        ds_put_format(ds, "%"PRIu16, nl_attr_get_u32(a));
+        ds_put_format(ds, "%"PRIu32, nl_attr_get_u32(a));
         break;
     case OVS_ACTION_ATTR_USERSPACE:
         format_odp_userspace_action(ds, a);
@@ -456,7 +485,10 @@ parse_odp_action(const char *s, const struct simap *port_names,
     {
         unsigned long long int pid;
         unsigned long long int output;
-        char userdata_s[32];
+        unsigned long long int probability;
+        unsigned long long int collector_set_id;
+        unsigned long long int obs_domain_id;
+        unsigned long long int obs_point_id;
         int vid, pcp;
         int n = -1;
 
@@ -477,37 +509,55 @@ parse_odp_action(const char *s, const struct simap *port_names,
             cookie.type = USER_ACTION_COOKIE_SFLOW;
             cookie.sflow.vlan_tci = htons(tci);
             cookie.sflow.output = output;
-            odp_put_userspace_action(pid, &cookie, sizeof cookie, actions);
+            odp_put_userspace_action(pid, &cookie, sizeof cookie.sflow,
+                                     actions);
             return n;
-        } else if (sscanf(s, "userspace(pid=%lli,slow_path%n", &pid, &n) > 0
+        } else if (sscanf(s, "userspace(pid=%lli,slow_path(%n", &pid, &n) > 0
                    && n > 0) {
             union user_action_cookie cookie;
-            int res;
+            char reason[32];
+
+            if (s[n] == ')' && s[n + 1] == ')') {
+                reason[0] = '\0';
+                n += 2;
+            } else if (sscanf(s + n, "%31[^)]))", reason) > 0) {
+                n += strlen(reason) + 2;
+            } else {
+                return -EINVAL;
+            }
 
             cookie.type = USER_ACTION_COOKIE_SLOW_PATH;
             cookie.slow_path.unused = 0;
-            cookie.slow_path.reason = 0;
+            cookie.slow_path.reason = string_to_slow_path_reason(reason);
 
-            res = parse_flags(&s[n], slow_path_reason_to_string,
-                              &cookie.slow_path.reason);
-            if (res < 0) {
-                return res;
-            }
-            n += res;
-            if (s[n] != ')') {
+            if (reason[0] && !cookie.slow_path.reason) {
                 return -EINVAL;
             }
-            n++;
 
-            odp_put_userspace_action(pid, &cookie, sizeof cookie, actions);
+            odp_put_userspace_action(pid, &cookie, sizeof cookie.slow_path,
+                                     actions);
             return n;
-        } else if (sscanf(s, "userspace(pid=%lli,userdata="
-                          "%31[x0123456789abcdefABCDEF])%n", &pid, userdata_s,
-                          &n) > 0 && n > 0) {
-            uint64_t userdata;
+        } else if (sscanf(s, "userspace(pid=%lli,flow_sample(probability=%lli,"
+                          "collector_set_id=%lli,obs_domain_id=%lli,"
+                          "obs_point_id=%lli))%n",
+                          &pid, &probability, &collector_set_id,
+                          &obs_domain_id, &obs_point_id, &n) > 0 && n > 0) {
+            union user_action_cookie cookie;
 
-            userdata = strtoull(userdata_s, NULL, 0);
-            odp_put_userspace_action(pid, &userdata, sizeof(userdata),
+            cookie.type = USER_ACTION_COOKIE_FLOW_SAMPLE;
+            cookie.flow_sample.probability = probability;
+            cookie.flow_sample.collector_set_id = collector_set_id;
+            cookie.flow_sample.obs_domain_id = obs_domain_id;
+            cookie.flow_sample.obs_point_id = obs_point_id;
+            odp_put_userspace_action(pid, &cookie, sizeof cookie.flow_sample,
+                                     actions);
+            return n;
+        } else if (sscanf(s, "userspace(pid=%lli,ipfix)%n", &pid, &n) > 0
+                   && n > 0) {
+            union user_action_cookie cookie;
+
+            cookie.type = USER_ACTION_COOKIE_IPFIX;
+            odp_put_userspace_action(pid, &cookie, sizeof cookie.ipfix,
                                      actions);
             return n;
         } else if (sscanf(s, "userspace(pid=%lli,userdata(%n", &pid, &n) > 0
@@ -737,8 +787,8 @@ tunnel_key_attr_len(int type)
     return -1;
 }
 
-static enum odp_key_fitness
-tun_key_from_attr(const struct nlattr *attr, struct flow_tnl *tun)
+enum odp_key_fitness
+odp_tun_key_from_attr(const struct nlattr *attr, struct flow_tnl *tun)
 {
     unsigned int left;
     const struct nlattr *a;
@@ -839,9 +889,10 @@ format_odp_key_attr(const struct nlattr *a, struct ds *ds)
     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;
 
-    ds_put_cstr(ds, ovs_key_attr_to_string(attr));
+    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)",
@@ -870,7 +921,7 @@ format_odp_key_attr(const struct nlattr *a, struct ds *ds)
 
     case OVS_KEY_ATTR_TUNNEL:
         memset(&tun_key, 0, sizeof tun_key);
-        if (tun_key_from_attr(a, &tun_key) == ODP_FIT_ERROR) {
+        if (odp_tun_key_from_attr(a, &tun_key) == ODP_FIT_ERROR) {
             ds_put_format(ds, "(error)");
         } else {
             ds_put_format(ds, "(tun_id=0x%"PRIx64",src="IP_FMT",dst="IP_FMT","
@@ -906,7 +957,7 @@ format_odp_key_attr(const struct nlattr *a, struct ds *ds)
     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_top_lse);
+        format_mpls_lse(ds, mpls_key->mpls_lse);
         ds_put_char(ds, ')');
         break;
     }
@@ -1231,7 +1282,7 @@ parse_odp_key_attr(const char *s, const struct simap *port_names,
 
             mpls = nl_msg_put_unspec_uninit(key, OVS_KEY_ATTR_MPLS,
                                             sizeof *mpls);
-            mpls->mpls_top_lse = mpls_lse_from_components(label, tc, ttl, bos);
+            mpls->mpls_lse = mpls_lse_from_components(label, tc, ttl, bos);
             return n;
         }
     }
@@ -1594,7 +1645,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_top_lse = flow->mpls_lse;
+        mpls_key->mpls_lse = flow->mpls_lse;
     }
 
     if (is_ip_any(flow) && !(flow->nw_frag & FLOW_NW_FRAG_LATER)) {
@@ -1671,7 +1722,10 @@ log_odp_key_attributes(struct vlog_rate_limit *rl, const char *title,
     ds_init(&s);
     for (i = 0; i < 64; i++) {
         if (attrs & (UINT64_C(1) << i)) {
-            ds_put_format(&s, " %s", ovs_key_attr_to_string(i));
+            char namebuf[OVS_KEY_ATTR_BUFSIZE];
+
+            ds_put_format(&s, " %s",
+                          ovs_key_attr_to_string(i, namebuf, sizeof namebuf));
         }
     }
     if (out_of_range_attr) {
@@ -1714,6 +1768,7 @@ parse_flow_nlattrs(const struct nlattr *key, size_t key_len,
     uint64_t present_attrs;
     size_t left;
 
+    BUILD_ASSERT(OVS_KEY_ATTR_MAX < CHAR_BIT * sizeof present_attrs);
     present_attrs = 0;
     *out_of_range_attrp = 0;
     NL_ATTR_FOR_EACH (nla, left, key, key_len) {
@@ -1722,18 +1777,24 @@ parse_flow_nlattrs(const struct nlattr *key, size_t key_len,
         int expected_len = odp_flow_key_attr_len(type);
 
         if (len != expected_len && expected_len >= 0) {
+            char namebuf[OVS_KEY_ATTR_BUFSIZE];
+
             VLOG_ERR_RL(&rl, "attribute %s has length %zu but should have "
-                        "length %d", ovs_key_attr_to_string(type),
+                        "length %d", ovs_key_attr_to_string(type, namebuf,
+                                                            sizeof namebuf),
                         len, expected_len);
             return false;
         }
 
-        if (type >= CHAR_BIT * sizeof present_attrs) {
+        if (type > OVS_KEY_ATTR_MAX) {
             *out_of_range_attrp = type;
         } else {
             if (present_attrs & (UINT64_C(1) << type)) {
+                char namebuf[OVS_KEY_ATTR_BUFSIZE];
+
                 VLOG_ERR_RL(&rl, "duplicate %s attribute in flow key",
-                            ovs_key_attr_to_string(type));
+                            ovs_key_attr_to_string(type,
+                                                   namebuf, sizeof namebuf));
                 return false;
             }
 
@@ -1805,32 +1866,16 @@ parse_l2_5_onward(const struct nlattr *attrs[OVS_KEY_ATTR_MAX + 1],
                   const struct nlattr *key, size_t key_len)
 {
     static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
-    ovs_be16 dl_type;
 
-    /* Parse MPLS label stack entry */
     if (eth_type_mpls(flow->dl_type)) {
-        /* Calculate fitness of outer attributes. */
         expected_attrs |= (UINT64_C(1) << OVS_KEY_ATTR_MPLS);
 
-        /* Get the MPLS LSE value. */
         if (!(present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_MPLS))) {
             return ODP_FIT_TOO_LITTLE;
         }
         flow->mpls_lse = nl_attr_get_be32(attrs[OVS_KEY_ATTR_MPLS]);
         flow->mpls_depth++;
-
-        if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_IPV4)) {
-            flow->encap_dl_type = htons(ETH_TYPE_IP);
-        } else if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_IPV6)) {
-            flow->encap_dl_type = htons(ETH_TYPE_IPV6);
-        } else if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_ARP)) {
-            flow->encap_dl_type = htons(ETH_TYPE_ARP);
-        }
-    }
-
-    dl_type = flow_innermost_dl_type(flow);
-
-    if (dl_type == htons(ETH_TYPE_IP)) {
+    } else if (flow->dl_type == htons(ETH_TYPE_IP)) {
         expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_IPV4;
         if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_IPV4)) {
             const struct ovs_key_ipv4 *ipv4_key;
@@ -1845,7 +1890,7 @@ parse_l2_5_onward(const struct nlattr *attrs[OVS_KEY_ATTR_MAX + 1],
                 return ODP_FIT_ERROR;
             }
         }
-    } else if (dl_type == htons(ETH_TYPE_IPV6)) {
+    } else if (flow->dl_type == htons(ETH_TYPE_IPV6)) {
         expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_IPV6;
         if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_IPV6)) {
             const struct ovs_key_ipv6 *ipv6_key;
@@ -1861,8 +1906,8 @@ parse_l2_5_onward(const struct nlattr *attrs[OVS_KEY_ATTR_MAX + 1],
                 return ODP_FIT_ERROR;
             }
         }
-    } else if (dl_type == htons(ETH_TYPE_ARP) ||
-               dl_type == htons(ETH_TYPE_RARP)) {
+    } else if (flow->dl_type == htons(ETH_TYPE_ARP) ||
+               flow->dl_type == htons(ETH_TYPE_RARP)) {
         expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_ARP;
         if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_ARP)) {
             const struct ovs_key_arp *arp_key;
@@ -1882,8 +1927,8 @@ parse_l2_5_onward(const struct nlattr *attrs[OVS_KEY_ATTR_MAX + 1],
     }
 
     if (flow->nw_proto == IPPROTO_TCP
-        && (dl_type == htons(ETH_TYPE_IP) ||
-            dl_type == htons(ETH_TYPE_IPV6))
+        && (flow->dl_type == htons(ETH_TYPE_IP) ||
+            flow->dl_type == htons(ETH_TYPE_IPV6))
         && !(flow->nw_frag & FLOW_NW_FRAG_LATER)) {
         expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_TCP;
         if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_TCP)) {
@@ -1894,8 +1939,8 @@ parse_l2_5_onward(const struct nlattr *attrs[OVS_KEY_ATTR_MAX + 1],
             flow->tp_dst = tcp_key->tcp_dst;
         }
     } else if (flow->nw_proto == IPPROTO_UDP
-               && (dl_type == htons(ETH_TYPE_IP) ||
-                   dl_type == htons(ETH_TYPE_IPV6))
+               && (flow->dl_type == htons(ETH_TYPE_IP) ||
+                   flow->dl_type == htons(ETH_TYPE_IPV6))
                && !(flow->nw_frag & FLOW_NW_FRAG_LATER)) {
         expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_UDP;
         if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_UDP)) {
@@ -1906,7 +1951,7 @@ parse_l2_5_onward(const struct nlattr *attrs[OVS_KEY_ATTR_MAX + 1],
             flow->tp_dst = udp_key->udp_dst;
         }
     } else if (flow->nw_proto == IPPROTO_ICMP
-               && dl_type == htons(ETH_TYPE_IP)
+               && flow->dl_type == htons(ETH_TYPE_IP)
                && !(flow->nw_frag & FLOW_NW_FRAG_LATER)) {
         expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_ICMP;
         if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_ICMP)) {
@@ -1917,7 +1962,7 @@ parse_l2_5_onward(const struct nlattr *attrs[OVS_KEY_ATTR_MAX + 1],
             flow->tp_dst = htons(icmp_key->icmp_code);
         }
     } else if (flow->nw_proto == IPPROTO_ICMPV6
-               && dl_type == htons(ETH_TYPE_IPV6)
+               && flow->dl_type == htons(ETH_TYPE_IPV6)
                && !(flow->nw_frag & FLOW_NW_FRAG_LATER)) {
         expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_ICMPV6;
         if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_ICMPV6)) {
@@ -1963,7 +2008,7 @@ parse_8021q_onward(const struct nlattr *attrs[OVS_KEY_ATTR_MAX + 1],
     enum odp_key_fitness fitness;
     ovs_be16 tci;
 
-    /* Calulate fitness of outer attributes. */
+    /* Calculate fitness of outer attributes. */
     expected_attrs |= ((UINT64_C(1) << OVS_KEY_ATTR_VLAN) |
                        (UINT64_C(1) << OVS_KEY_ATTR_ENCAP));
     fitness = check_expectations(present_attrs, out_of_range_attr,
@@ -2055,7 +2100,7 @@ odp_flow_key_to_flow(const struct nlattr *key, size_t key_len,
     if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_TUNNEL)) {
         enum odp_key_fitness res;
 
-        res = tun_key_from_attr(attrs[OVS_KEY_ATTR_TUNNEL], &flow->tunnel);
+        res = odp_tun_key_from_attr(attrs[OVS_KEY_ATTR_TUNNEL], &flow->tunnel);
         if (res == ODP_FIT_ERROR) {
             return ODP_FIT_ERROR;
         } else if (res == ODP_FIT_PERFECT) {
@@ -2176,20 +2221,20 @@ void
 commit_odp_tunnel_action(const struct flow *flow, struct flow *base,
                          struct ofpbuf *odp_actions)
 {
-    if (!memcmp(&base->tunnel, &flow->tunnel, sizeof base->tunnel)) {
-        return;
-    }
-    memcpy(&base->tunnel, &flow->tunnel, sizeof base->tunnel);
-
     /* A valid IPV4_TUNNEL must have non-zero ip_dst. */
     if (flow->tunnel.ip_dst) {
+        if (!memcmp(&base->tunnel, &flow->tunnel, sizeof base->tunnel)) {
+            return;
+        }
+        memcpy(&base->tunnel, &flow->tunnel, sizeof base->tunnel);
         odp_put_tunnel_action(&base->tunnel, odp_actions);
     }
 }
 
 static void
 commit_set_ether_addr_action(const struct flow *flow, struct flow *base,
-                             struct ofpbuf *odp_actions)
+                             struct ofpbuf *odp_actions,
+                             struct flow_wildcards *wc)
 {
     struct ovs_key_ethernet eth_key;
 
@@ -2198,6 +2243,9 @@ commit_set_ether_addr_action(const struct flow *flow, struct flow *base,
         return;
     }
 
+    memset(&wc->masks.dl_src, 0xff, sizeof wc->masks.dl_src);
+    memset(&wc->masks.dl_dst, 0xff, sizeof wc->masks.dl_dst);
+
     memcpy(base->dl_src, flow->dl_src, ETH_ADDR_LEN);
     memcpy(base->dl_dst, flow->dl_dst, ETH_ADDR_LEN);
 
@@ -2210,12 +2258,14 @@ commit_set_ether_addr_action(const struct flow *flow, struct flow *base,
 
 static void
 commit_vlan_action(const struct flow *flow, struct flow *base,
-                   struct ofpbuf *odp_actions)
+                   struct ofpbuf *odp_actions, struct flow_wildcards *wc)
 {
     if (base->vlan_tci == flow->vlan_tci) {
         return;
     }
 
+    memset(&wc->masks.vlan_tci, 0xff, sizeof wc->masks.vlan_tci);
+
     if (base->vlan_tci & htons(VLAN_CFI)) {
         nl_msg_put_flag(odp_actions, OVS_ACTION_ATTR_POP_VLAN);
     }
@@ -2233,13 +2283,15 @@ commit_vlan_action(const struct flow *flow, struct flow *base,
 
 static void
 commit_mpls_action(const struct flow *flow, struct flow *base,
-                   struct ofpbuf *odp_actions)
+                   struct ofpbuf *odp_actions, struct flow_wildcards *wc)
 {
     if (flow->mpls_lse == base->mpls_lse &&
         flow->mpls_depth == base->mpls_depth) {
         return;
     }
 
+    memset(&wc->masks.mpls_lse, 0xff, sizeof wc->masks.mpls_lse);
+
     if (flow->mpls_depth < base->mpls_depth) {
         if (base->mpls_depth - flow->mpls_depth > 1) {
             static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(10, 10);
@@ -2265,7 +2317,7 @@ commit_mpls_action(const struct flow *flow, struct flow *base,
     } else {
         struct ovs_key_mpls mpls_key;
 
-        mpls_key.mpls_top_lse = flow->mpls_lse;
+        mpls_key.mpls_lse = flow->mpls_lse;
         commit_set_action(odp_actions, OVS_KEY_ATTR_MPLS,
                           &mpls_key, sizeof(mpls_key));
     }
@@ -2277,7 +2329,7 @@ commit_mpls_action(const struct flow *flow, struct flow *base,
 
 static void
 commit_set_ipv4_action(const struct flow *flow, struct flow *base,
-                     struct ofpbuf *odp_actions)
+                     struct ofpbuf *odp_actions, struct flow_wildcards *wc)
 {
     struct ovs_key_ipv4 ipv4_key;
 
@@ -2289,6 +2341,13 @@ commit_set_ipv4_action(const struct flow *flow, struct flow *base,
         return;
     }
 
+    memset(&wc->masks.nw_src, 0xff, sizeof wc->masks.nw_src);
+    memset(&wc->masks.nw_dst, 0xff, sizeof wc->masks.nw_dst);
+    memset(&wc->masks.nw_tos, 0xff, sizeof wc->masks.nw_tos);
+    memset(&wc->masks.nw_ttl, 0xff, sizeof wc->masks.nw_ttl);
+    memset(&wc->masks.nw_proto, 0xff, sizeof wc->masks.nw_proto);
+    memset(&wc->masks.nw_frag, 0xff, sizeof wc->masks.nw_frag);
+
     ipv4_key.ipv4_src = base->nw_src = flow->nw_src;
     ipv4_key.ipv4_dst = base->nw_dst = flow->nw_dst;
     ipv4_key.ipv4_tos = base->nw_tos = flow->nw_tos;
@@ -2302,7 +2361,7 @@ commit_set_ipv4_action(const struct flow *flow, struct flow *base,
 
 static void
 commit_set_ipv6_action(const struct flow *flow, struct flow *base,
-                       struct ofpbuf *odp_actions)
+                       struct ofpbuf *odp_actions, struct flow_wildcards *wc)
 {
     struct ovs_key_ipv6 ipv6_key;
 
@@ -2315,6 +2374,14 @@ commit_set_ipv6_action(const struct flow *flow, struct flow *base,
         return;
     }
 
+    memset(&wc->masks.ipv6_src, 0xff, sizeof wc->masks.ipv6_src);
+    memset(&wc->masks.ipv6_dst, 0xff, sizeof wc->masks.ipv6_dst);
+    memset(&wc->masks.ipv6_label, 0xff, sizeof wc->masks.ipv6_label);
+    memset(&wc->masks.nw_tos, 0xff, sizeof wc->masks.nw_tos);
+    memset(&wc->masks.nw_ttl, 0xff, sizeof wc->masks.nw_ttl);
+    memset(&wc->masks.nw_proto, 0xff, sizeof wc->masks.nw_proto);
+    memset(&wc->masks.nw_frag, 0xff, sizeof wc->masks.nw_frag);
+
     base->ipv6_src = flow->ipv6_src;
     memcpy(&ipv6_key.ipv6_src, &base->ipv6_src, sizeof(ipv6_key.ipv6_src));
     base->ipv6_dst = flow->ipv6_dst;
@@ -2332,27 +2399,25 @@ commit_set_ipv6_action(const struct flow *flow, struct flow *base,
 
 static void
 commit_set_nw_action(const struct flow *flow, struct flow *base,
-                     struct ofpbuf *odp_actions)
+                     struct ofpbuf *odp_actions, struct flow_wildcards *wc)
 {
-    ovs_be16 dl_type = flow_innermost_dl_type(flow);
-
     /* Check if flow really have an IP header. */
     if (!flow->nw_proto) {
         return;
     }
 
-    if (dl_type == htons(ETH_TYPE_IP)) {
-        commit_set_ipv4_action(flow, base, odp_actions);
-    } else if (dl_type == htons(ETH_TYPE_IPV6)) {
-        commit_set_ipv6_action(flow, base, odp_actions);
+    if (base->dl_type == htons(ETH_TYPE_IP)) {
+        commit_set_ipv4_action(flow, base, odp_actions, wc);
+    } else if (base->dl_type == htons(ETH_TYPE_IPV6)) {
+        commit_set_ipv6_action(flow, base, odp_actions, wc);
     }
 }
 
 static void
 commit_set_port_action(const struct flow *flow, struct flow *base,
-                       struct ofpbuf *odp_actions)
+                       struct ofpbuf *odp_actions, struct flow_wildcards *wc)
 {
-    if (!base->tp_src && !base->tp_dst) {
+    if (!is_ip_any(base) || (!base->tp_src && !base->tp_dst)) {
         return;
     }
 
@@ -2361,6 +2426,9 @@ commit_set_port_action(const struct flow *flow, struct flow *base,
         return;
     }
 
+    memset(&wc->masks.tp_src, 0xff, sizeof wc->masks.tp_src);
+    memset(&wc->masks.tp_dst, 0xff, sizeof wc->masks.tp_dst);
+
     if (flow->nw_proto == IPPROTO_TCP) {
         struct ovs_key_tcp port_key;
 
@@ -2383,11 +2451,14 @@ commit_set_port_action(const struct flow *flow, struct flow *base,
 
 static void
 commit_set_priority_action(const struct flow *flow, struct flow *base,
-                           struct ofpbuf *odp_actions)
+                           struct ofpbuf *odp_actions,
+                           struct flow_wildcards *wc)
 {
     if (base->skb_priority == flow->skb_priority) {
         return;
     }
+
+    memset(&wc->masks.skb_priority, 0xff, sizeof wc->masks.skb_priority);
     base->skb_priority = flow->skb_priority;
 
     commit_set_action(odp_actions, OVS_KEY_ATTR_PRIORITY,
@@ -2396,11 +2467,14 @@ commit_set_priority_action(const struct flow *flow, struct flow *base,
 
 static void
 commit_set_skb_mark_action(const struct flow *flow, struct flow *base,
-                           struct ofpbuf *odp_actions)
+                           struct ofpbuf *odp_actions,
+                           struct flow_wildcards *wc)
 {
     if (base->skb_mark == flow->skb_mark) {
         return;
     }
+
+    memset(&wc->masks.skb_mark, 0xff, sizeof wc->masks.skb_mark);
     base->skb_mark = flow->skb_mark;
 
     odp_put_skb_mark_action(base->skb_mark, odp_actions);
@@ -2409,16 +2483,21 @@ commit_set_skb_mark_action(const struct flow *flow, struct flow *base,
  * 'base' and 'flow', appends ODP actions to 'odp_actions' that change the flow
  * key from 'base' into 'flow', and then changes 'base' the same way.  Does not
  * commit set_tunnel actions.  Users should call commit_odp_tunnel_action()
- * in addition to this function if needed. */
+ * in addition to this function if needed.  Sets fields in 'wc' that are
+ * used as part of the action. */
 void
 commit_odp_actions(const struct flow *flow, struct flow *base,
-                   struct ofpbuf *odp_actions)
+                   struct ofpbuf *odp_actions, struct flow_wildcards *wc)
 {
-    commit_set_ether_addr_action(flow, base, odp_actions);
-    commit_vlan_action(flow, base, odp_actions);
-    commit_mpls_action(flow, base, odp_actions);
-    commit_set_nw_action(flow, base, odp_actions);
-    commit_set_port_action(flow, base, odp_actions);
-    commit_set_priority_action(flow, base, odp_actions);
-    commit_set_skb_mark_action(flow, base, odp_actions);
+    commit_set_ether_addr_action(flow, base, odp_actions, wc);
+    commit_vlan_action(flow, base, odp_actions, wc);
+    commit_set_nw_action(flow, base, odp_actions, wc);
+    commit_set_port_action(flow, base, odp_actions, wc);
+    /* Committing MPLS actions should occur after committing nw and port
+     * actions. This is because committing MPLS actions may alter a packet so
+     * that it is no longer IP and thus nw and port actions are no longer valid.
+     */
+    commit_mpls_action(flow, base, odp_actions, wc);
+    commit_set_priority_action(flow, base, odp_actions, wc);
+    commit_set_skb_mark_action(flow, base, odp_actions, wc);
 }