Signed-off-by: Jarno Rajahalme <jrajaha...@nicira.com>
---
 lib/odp-util.c               |  368 ++++++++++++++++++++++++++----------------
 lib/odp-util.h               |    5 +-
 ofproto/ofproto-dpif-xlate.c |   15 +-
 tests/ofproto-dpif.at        |   62 ++++---
 tests/tunnel.at              |    8 +-
 5 files changed, 275 insertions(+), 183 deletions(-)

diff --git a/lib/odp-util.c b/lib/odp-util.c
index 41ab183..b9b6e2a 100644
--- a/lib/odp-util.c
+++ b/lib/odp-util.c
@@ -3629,14 +3629,6 @@ commit_masked_set_action(struct ofpbuf *odp_actions,
     nl_msg_end_nested(odp_actions, offset);
 }
 
-void
-odp_put_pkt_mark_action(const uint32_t pkt_mark,
-                        struct ofpbuf *odp_actions)
-{
-    commit_set_action(odp_actions, OVS_KEY_ATTR_SKB_MARK, &pkt_mark,
-                      sizeof(pkt_mark));
-}
-
 /* If any of the flow key data that ODP actions can modify are different in
  * 'base->tunnel' and 'flow->tunnel', appends a set_tunnel ODP action to
  * 'odp_actions' that change the flow tunneling information in key from
@@ -3660,26 +3652,44 @@ commit_odp_tunnel_action(const struct flow *flow, 
struct flow *base,
 static void
 commit_set_ether_addr_action(const struct flow *flow, struct flow *base,
                              struct ofpbuf *odp_actions,
-                             struct flow_wildcards *wc)
+                             struct flow_wildcards *wc,
+                             bool use_masked)
 {
-    struct ovs_key_ethernet eth_key;
-
-    if (eth_addr_equals(base->dl_src, flow->dl_src) &&
-        eth_addr_equals(base->dl_dst, flow->dl_dst)) {
+    struct ovs_key_ethernet key, mask;
+    bool fully_masked;
+
+    /* Mask bits are set when we have either read or set the corresponding
+     * values.  Masked bits will be exact-matched, no need to set them
+     * if the value did not actually change. */
+    if (eth_addr_equals(flow->dl_src, base->dl_src) &&
+        eth_addr_equals(flow->dl_dst, base->dl_dst)) {
         return;
     }
 
-    memset(&wc->masks.dl_src, 0xff, sizeof wc->masks.dl_src);
-    memset(&wc->masks.dl_dst, 0xff, sizeof wc->masks.dl_dst);
+    memcpy(key.eth_src, flow->dl_src, ETH_ADDR_LEN);
+    memcpy(key.eth_dst, flow->dl_dst, ETH_ADDR_LEN);
 
-    memcpy(base->dl_src, flow->dl_src, ETH_ADDR_LEN);
-    memcpy(base->dl_dst, flow->dl_dst, ETH_ADDR_LEN);
+    fully_masked = eth_mask_is_exact(wc->masks.dl_src)
+        && eth_mask_is_exact(wc->masks.dl_dst);
 
-    memcpy(eth_key.eth_src, base->dl_src, ETH_ADDR_LEN);
-    memcpy(eth_key.eth_dst, base->dl_dst, ETH_ADDR_LEN);
+    if (use_masked && !fully_masked) {
+        memcpy(mask.eth_src, wc->masks.dl_src, ETH_ADDR_LEN);
+        memcpy(mask.eth_dst, wc->masks.dl_dst, ETH_ADDR_LEN);
 
-    commit_set_action(odp_actions, OVS_KEY_ATTR_ETHERNET,
-                      &eth_key, sizeof(eth_key));
+        commit_masked_set_action(odp_actions, OVS_KEY_ATTR_ETHERNET, &key,
+                                 &mask, sizeof key);
+    } else {
+        if (!fully_masked) {
+            memset(&wc->masks.dl_src, 0xff, sizeof wc->masks.dl_src);
+            memset(&wc->masks.dl_dst, 0xff, sizeof wc->masks.dl_dst);
+        }
+
+        commit_set_action(odp_actions, OVS_KEY_ATTR_ETHERNET, &key,
+                          sizeof key);
+    }
+
+    memcpy(base->dl_src, flow->dl_src, ETH_ADDR_LEN);
+    memcpy(base->dl_dst, flow->dl_dst, ETH_ADDR_LEN);
 }
 
 static void
@@ -3782,93 +3792,155 @@ 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 flow_wildcards *wc)
+                       struct ofpbuf *odp_actions, struct flow_wildcards *wc,
+                       bool use_masked)
 {
-    struct ovs_key_ipv4 ipv4_key;
-
-    if (base->nw_src == flow->nw_src &&
-        base->nw_dst == flow->nw_dst &&
-        base->nw_tos == flow->nw_tos &&
-        base->nw_ttl == flow->nw_ttl &&
-        base->nw_frag == flow->nw_frag) {
+    struct ovs_key_ipv4 key, mask;
+
+    /* Check that non-masked bits are intact, and that nw_proto and nw_frag
+     * remain unchanged. */
+    ovs_assert(!((flow->nw_src ^ base->nw_src) & ~wc->masks.nw_src)
+               && !((flow->nw_dst ^ base->nw_dst) & ~wc->masks.nw_dst)
+               && !((flow->nw_tos ^ base->nw_tos) & ~wc->masks.nw_tos)
+               && !((flow->nw_ttl ^ base->nw_ttl) & ~wc->masks.nw_ttl)
+               && flow->nw_proto == base->nw_proto
+               && flow->nw_frag == base->nw_frag);
+
+    /* Mask bits are set when we have either read or set the corresponding
+     * values.  Masked bits will be exact-matched, no need to set them
+     * if the value did not actually change. */
+    if ((flow->nw_src == base->nw_src) && (flow->nw_dst == base->nw_dst) &&
+        (flow->nw_tos == base->nw_tos) && (flow->nw_ttl == base->nw_ttl)) {
         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);
+    key.ipv4_src = flow->nw_src;
+    key.ipv4_dst = flow->nw_dst;
+    key.ipv4_tos = flow->nw_tos;
+    key.ipv4_ttl = flow->nw_ttl;
+    key.ipv4_proto = base->nw_proto;
+    key.ipv4_frag = ovs_to_odp_frag(base->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;
-    ipv4_key.ipv4_ttl = base->nw_ttl = flow->nw_ttl;
-    ipv4_key.ipv4_proto = base->nw_proto;
-    ipv4_key.ipv4_frag = ovs_to_odp_frag(base->nw_frag);
+    if (use_masked) {
+        mask.ipv4_src = wc->masks.nw_src;
+        mask.ipv4_dst = wc->masks.nw_dst;
+        mask.ipv4_tos = wc->masks.nw_tos;
+        mask.ipv4_ttl = wc->masks.nw_ttl;
+        mask.ipv4_proto = 0; /* Not writeable. */
+        mask.ipv4_frag = 0;  /* Not writeable. */
 
-    commit_set_action(odp_actions, OVS_KEY_ATTR_IPV4,
-                      &ipv4_key, sizeof(ipv4_key));
+        commit_masked_set_action(odp_actions, OVS_KEY_ATTR_IPV4, &key, &mask,
+                                 sizeof key);
+    } else {
+        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);
+
+        commit_set_action(odp_actions, OVS_KEY_ATTR_IPV4, &key, sizeof key);
+    }
+
+    base->nw_src = flow->nw_src;
+    base->nw_dst = flow->nw_dst;
+    base->nw_tos = flow->nw_tos;
+    base->nw_ttl = flow->nw_ttl;
 }
 
 static void
 commit_set_ipv6_action(const struct flow *flow, struct flow *base,
-                       struct ofpbuf *odp_actions, struct flow_wildcards *wc)
+                       struct ofpbuf *odp_actions, struct flow_wildcards *wc,
+                       bool use_masked)
 {
-    struct ovs_key_ipv6 ipv6_key;
-
-    if (ipv6_addr_equals(&base->ipv6_src, &flow->ipv6_src) &&
-        ipv6_addr_equals(&base->ipv6_dst, &flow->ipv6_dst) &&
-        base->ipv6_label == flow->ipv6_label &&
-        base->nw_tos == flow->nw_tos &&
-        base->nw_ttl == flow->nw_ttl &&
-        base->nw_frag == flow->nw_frag) {
+    struct ovs_key_ipv6 key, mask;
+
+    ovs_assert(!((flow->ipv6_label ^ base->ipv6_label) & ~wc->masks.ipv6_label)
+               && !((flow->nw_tos ^ base->nw_tos) & ~wc->masks.nw_tos)
+               && !((flow->nw_ttl ^ base->nw_ttl) & ~wc->masks.nw_ttl)
+               && flow->nw_proto == base->nw_proto
+               && flow->nw_frag == base->nw_frag);
+
+    /* Mask bits are set when we have either read or set the corresponding
+     * values.  Masked bits will be exact-matched, no need to set them
+     * if the value did not actually change. */
+    if (ipv6_addr_equals(&flow->ipv6_src, &base->ipv6_src) &&
+        ipv6_addr_equals(&flow->ipv6_dst, &base->ipv6_dst) &&
+        flow->ipv6_label == base->ipv6_label &&
+        flow->nw_tos == base->nw_tos &&
+        flow->nw_ttl == base->nw_ttl) {
         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);
+    memcpy(&key.ipv6_src, &flow->ipv6_src, sizeof(key.ipv6_src));
+    memcpy(&key.ipv6_dst, &flow->ipv6_dst, sizeof(key.ipv6_dst));
+    key.ipv6_label = flow->ipv6_label;
+    key.ipv6_tclass = flow->nw_tos;
+    key.ipv6_hlimit = flow->nw_ttl;
+    key.ipv6_proto = base->nw_proto;
+    key.ipv6_frag = ovs_to_odp_frag(base->nw_frag);
+
+    if (use_masked) {
+        *(struct in6_addr *)&mask.ipv6_src = wc->masks.ipv6_src;
+        *(struct in6_addr *)&mask.ipv6_dst = wc->masks.ipv6_dst;
+        mask.ipv6_label = wc->masks.ipv6_label;
+        mask.ipv6_tclass = wc->masks.nw_tos;
+        mask.ipv6_hlimit = wc->masks.nw_ttl;
+        mask.ipv6_proto = 0; /* Not writeable. */
+        mask.ipv6_frag = 0;  /* Not writeable. */
+
+        commit_masked_set_action(odp_actions, OVS_KEY_ATTR_IPV6, &key,
+                                 &mask, sizeof key);
+    } else {
+        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);
+
+        commit_set_action(odp_actions, OVS_KEY_ATTR_IPV6, &key, sizeof key);
+    }
 
     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;
-    memcpy(&ipv6_key.ipv6_dst, &base->ipv6_dst, sizeof(ipv6_key.ipv6_dst));
-
-    ipv6_key.ipv6_label = base->ipv6_label = flow->ipv6_label;
-    ipv6_key.ipv6_tclass = base->nw_tos = flow->nw_tos;
-    ipv6_key.ipv6_hlimit = base->nw_ttl = flow->nw_ttl;
-    ipv6_key.ipv6_proto = base->nw_proto;
-    ipv6_key.ipv6_frag = ovs_to_odp_frag(base->nw_frag);
-
-    commit_set_action(odp_actions, OVS_KEY_ATTR_IPV6,
-                      &ipv6_key, sizeof(ipv6_key));
+    base->ipv6_label = flow->ipv6_label;
+    base->nw_tos = flow->nw_tos;
+    base->nw_ttl = flow->nw_ttl;
 }
 
 static enum slow_path_reason
 commit_set_arp_action(const struct flow *flow, struct flow *base,
                       struct ofpbuf *odp_actions, struct flow_wildcards *wc)
 {
-    struct ovs_key_arp arp_key;
-
-    if (base->nw_src == flow->nw_src &&
-        base->nw_dst == flow->nw_dst &&
-        base->nw_proto == flow->nw_proto &&
-        eth_addr_equals(base->arp_sha, flow->arp_sha) &&
-        eth_addr_equals(base->arp_tha, flow->arp_tha)) {
+    struct ovs_key_arp key, mask;
+
+    /* Mask bits are set when we have either read or set the corresponding
+     * values.  Masked bits will be exact-matched, no need to set them
+     * if the value did not actually change. */
+    if (flow->nw_src == base->nw_src &&
+        flow->nw_dst == base->nw_dst &&
+        flow->nw_proto == base->nw_proto &&
+        eth_addr_equals(flow->arp_sha, base->arp_sha) &&
+        eth_addr_equals(flow->arp_tha, base->arp_tha)) {
         return 0;
     }
 
-    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_proto, 0xff, sizeof wc->masks.nw_proto);
-    memset(&wc->masks.arp_sha, 0xff, sizeof wc->masks.arp_sha);
-    memset(&wc->masks.arp_tha, 0xff, sizeof wc->masks.arp_tha);
+    key.arp_sip = flow->nw_src;
+    key.arp_tip = flow->nw_dst;
+    key.arp_op = htons(flow->nw_proto);
+    memcpy(key.arp_sha, flow->arp_sha, ETH_ADDR_LEN);
+    memcpy(key.arp_tha, flow->arp_tha, ETH_ADDR_LEN);
+
+    mask.arp_sip = wc->masks.nw_src;
+    mask.arp_tip = wc->masks.nw_dst;
+    mask.arp_op = htons(wc->masks.nw_proto);
+    memcpy(mask.arp_sha, wc->masks.arp_sha, ETH_ADDR_LEN);
+    memcpy(mask.arp_tha, wc->masks.arp_tha, ETH_ADDR_LEN);
+
+    commit_masked_set_action(odp_actions, OVS_KEY_ATTR_ARP, &key, &mask,
+                             sizeof key);
 
     base->nw_src = flow->nw_src;
     base->nw_dst = flow->nw_dst;
@@ -3876,20 +3948,13 @@ commit_set_arp_action(const struct flow *flow, struct 
flow *base,
     memcpy(base->arp_sha, flow->arp_sha, ETH_ADDR_LEN);
     memcpy(base->arp_tha, flow->arp_tha, ETH_ADDR_LEN);
 
-    arp_key.arp_sip = base->nw_src;
-    arp_key.arp_tip = base->nw_dst;
-    arp_key.arp_op = htons(base->nw_proto);
-    memcpy(arp_key.arp_sha, flow->arp_sha, ETH_ADDR_LEN);
-    memcpy(arp_key.arp_tha, flow->arp_tha, ETH_ADDR_LEN);
-
-    commit_set_action(odp_actions, OVS_KEY_ATTR_ARP, &arp_key, sizeof arp_key);
-
     return SLOW_ACTION;
 }
 
 static enum slow_path_reason
 commit_set_nw_action(const struct flow *flow, struct flow *base,
-                     struct ofpbuf *odp_actions, struct flow_wildcards *wc)
+                     struct ofpbuf *odp_actions, struct flow_wildcards *wc,
+                     bool use_masked)
 {
     /* Check if 'flow' really has an L3 header. */
     if (!flow->nw_proto) {
@@ -3898,11 +3963,11 @@ commit_set_nw_action(const struct flow *flow, struct 
flow *base,
 
     switch (ntohs(base->dl_type)) {
     case ETH_TYPE_IP:
-        commit_set_ipv4_action(flow, base, odp_actions, wc);
+        commit_set_ipv4_action(flow, base, odp_actions, wc, use_masked);
         break;
 
     case ETH_TYPE_IPV6:
-        commit_set_ipv6_action(flow, base, odp_actions, wc);
+        commit_set_ipv6_action(flow, base, odp_actions, wc, use_masked);
         break;
 
     case ETH_TYPE_ARP:
@@ -3912,84 +3977,110 @@ commit_set_nw_action(const struct flow *flow, struct 
flow *base,
     return 0;
 }
 
+/* TCP, UDP, and SCTP keys have the same layout. */
+BUILD_ASSERT_DECL(sizeof(struct ovs_key_tcp) == sizeof(struct ovs_key_udp) &&
+                  sizeof(struct ovs_key_tcp) == sizeof(struct ovs_key_sctp));
+
 static void
 commit_set_port_action(const struct flow *flow, struct flow *base,
-                       struct ofpbuf *odp_actions, struct flow_wildcards *wc)
+                       struct ofpbuf *odp_actions, struct flow_wildcards *wc,
+                       bool use_masked)
 {
+    enum ovs_key_attr key_type;
+    struct ovs_key_tcp key, mask; /* Used for UDP and SCTP, too. */
+
     /* Check if 'flow' really has an L3 header. */
     if (!flow->nw_proto) {
         return;
     }
 
-    if (!is_ip_any(base) || (!base->tp_src && !base->tp_dst)) {
-        return;
-    }
+    ovs_assert(!((flow->tp_src ^ base->tp_src) & ~wc->masks.tp_src) &&
+               !((flow->tp_dst ^ base->tp_dst) & ~wc->masks.tp_dst));
 
-    if (base->tp_src == flow->tp_src &&
-        base->tp_dst == flow->tp_dst) {
+    if (!is_ip_any(base) ||
+        (flow->tp_src == base->tp_src && flow->tp_dst == base->tp_dst)) {
         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;
-
-        port_key.tcp_src = base->tp_src = flow->tp_src;
-        port_key.tcp_dst = base->tp_dst = flow->tp_dst;
-
-        commit_set_action(odp_actions, OVS_KEY_ATTR_TCP,
-                          &port_key, sizeof(port_key));
-
+        key_type = OVS_KEY_ATTR_TCP;
     } else if (flow->nw_proto == IPPROTO_UDP) {
-        struct ovs_key_udp port_key;
+        key_type = OVS_KEY_ATTR_UDP;
+    } else if (flow->nw_proto == IPPROTO_SCTP) {
+        key_type = OVS_KEY_ATTR_SCTP;
+    } else {
+        return;
+    }
 
-        port_key.udp_src = base->tp_src = flow->tp_src;
-        port_key.udp_dst = base->tp_dst = flow->tp_dst;
+    key.tcp_src = flow->tp_src;
+    key.tcp_dst = flow->tp_dst;
 
-        commit_set_action(odp_actions, OVS_KEY_ATTR_UDP,
-                          &port_key, sizeof(port_key));
-    } else if (flow->nw_proto == IPPROTO_SCTP) {
-        struct ovs_key_sctp port_key;
+    if (use_masked && (wc->masks.tp_src != OVS_BE16_MAX
+                       || wc->masks.tp_dst != OVS_BE16_MAX)) {
+        mask.tcp_src = wc->masks.tp_src;
+        mask.tcp_dst = wc->masks.tp_dst;
 
-        port_key.sctp_src = base->tp_src = flow->tp_src;
-        port_key.sctp_dst = base->tp_dst = flow->tp_dst;
+        commit_masked_set_action(odp_actions, key_type, &key, &mask,
+                                 sizeof key);
+    } else {
+        wc->masks.tp_src = OVS_BE16_MAX;
+        wc->masks.tp_dst = OVS_BE16_MAX;
 
-        commit_set_action(odp_actions, OVS_KEY_ATTR_SCTP,
-                          &port_key, sizeof(port_key));
+        commit_set_action(odp_actions, key_type, &key, sizeof key);
     }
+
+    base->tp_src = flow->tp_src;
+    base->tp_dst = flow->tp_dst;
 }
 
 static void
 commit_set_priority_action(const struct flow *flow, struct flow *base,
                            struct ofpbuf *odp_actions,
-                           struct flow_wildcards *wc)
+                           struct flow_wildcards *wc,
+                           bool use_masked)
 {
-    if (base->skb_priority == flow->skb_priority) {
+    ovs_assert(!((flow->skb_priority ^ base->skb_priority)
+                 & ~wc->masks.skb_priority));
+
+    if (flow->skb_priority == base->skb_priority) {
         return;
     }
 
-    memset(&wc->masks.skb_priority, 0xff, sizeof wc->masks.skb_priority);
-    base->skb_priority = flow->skb_priority;
+    if (use_masked && wc->masks.skb_priority != OVS_BE32_MAX) {
+        commit_masked_set_action(odp_actions, OVS_KEY_ATTR_PRIORITY,
+                                 &flow->skb_priority, &wc->masks.skb_priority,
+                                 sizeof(flow->skb_priority));
+    } else {
+        wc->masks.skb_priority = OVS_BE32_MAX;
+        commit_set_action(odp_actions, OVS_KEY_ATTR_PRIORITY,
+                          &flow->skb_priority, sizeof(flow->skb_priority));
+    }
 
-    commit_set_action(odp_actions, OVS_KEY_ATTR_PRIORITY,
-                      &base->skb_priority, sizeof(base->skb_priority));
+    base->skb_priority = flow->skb_priority;
 }
 
 static void
 commit_set_pkt_mark_action(const struct flow *flow, struct flow *base,
                            struct ofpbuf *odp_actions,
-                           struct flow_wildcards *wc)
+                           struct flow_wildcards *wc,
+                           bool use_masked)
 {
-    if (base->pkt_mark == flow->pkt_mark) {
+    ovs_assert(!((flow->pkt_mark ^ base->pkt_mark) & ~wc->masks.pkt_mark));
+
+    if (flow->pkt_mark == base->pkt_mark) {
         return;
     }
 
-    memset(&wc->masks.pkt_mark, 0xff, sizeof wc->masks.pkt_mark);
+    if (use_masked && wc->masks.pkt_mark != OVS_BE32_MAX) {
+        commit_masked_set_action(odp_actions, OVS_KEY_ATTR_SKB_MARK,
+                                 &flow->pkt_mark, &wc->masks.pkt_mark,
+                                 sizeof(flow->pkt_mark));
+    } else {
+        wc->masks.pkt_mark = OVS_BE32_MAX;
+        commit_set_action(odp_actions, OVS_KEY_ATTR_SKB_MARK, &flow->pkt_mark,
+                          sizeof(flow->pkt_mark));
+    }
     base->pkt_mark = flow->pkt_mark;
-
-    odp_put_pkt_mark_action(base->pkt_mark, odp_actions);
 }
 
 /* If any of the flow key data that ODP actions can modify are different in
@@ -4003,17 +4094,18 @@ commit_set_pkt_mark_action(const struct flow *flow, 
struct flow *base,
  * slow path, if there is one, otherwise 0. */
 enum slow_path_reason
 commit_odp_actions(const struct flow *flow, struct flow *base,
-                   struct ofpbuf *odp_actions, struct flow_wildcards *wc)
+                   struct ofpbuf *odp_actions, struct flow_wildcards *wc,
+                   bool use_masked)
 {
     enum slow_path_reason slow;
 
-    commit_set_ether_addr_action(flow, base, odp_actions, wc);
-    slow = commit_set_nw_action(flow, base, odp_actions, wc);
-    commit_set_port_action(flow, base, odp_actions, wc);
+    commit_set_ether_addr_action(flow, base, odp_actions, wc, use_masked);
+    slow = commit_set_nw_action(flow, base, odp_actions, wc, use_masked);
+    commit_set_port_action(flow, base, odp_actions, wc, use_masked);
     commit_mpls_action(flow, base, odp_actions, wc);
     commit_vlan_action(flow->vlan_tci, base, odp_actions, wc);
-    commit_set_priority_action(flow, base, odp_actions, wc);
-    commit_set_pkt_mark_action(flow, base, odp_actions, wc);
+    commit_set_priority_action(flow, base, odp_actions, wc, use_masked);
+    commit_set_pkt_mark_action(flow, base, odp_actions, wc, use_masked);
 
     return slow;
 }
diff --git a/lib/odp-util.h b/lib/odp-util.h
index 832ed2b..f71c591 100644
--- a/lib/odp-util.h
+++ b/lib/odp-util.h
@@ -189,7 +189,8 @@ void commit_masked_set_action(struct ofpbuf *odp_actions,
 enum slow_path_reason commit_odp_actions(const struct flow *,
                                          struct flow *base,
                                          struct ofpbuf *odp_actions,
-                                         struct flow_wildcards *wc);
+                                         struct flow_wildcards *wc,
+                                         bool use_masked);
 
 /* ofproto-dpif interface.
  *
@@ -242,7 +243,5 @@ size_t odp_put_userspace_action(uint32_t pid,
                                 struct ofpbuf *odp_actions);
 void odp_put_tunnel_action(const struct flow_tnl *tunnel,
                            struct ofpbuf *odp_actions);
-void odp_put_pkt_mark_action(const uint32_t pkt_mark,
-                             struct ofpbuf *odp_actions);
 
 #endif /* odp-util.h */
diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c
index d103a03..e799872 100644
--- a/ofproto/ofproto-dpif-xlate.c
+++ b/ofproto/ofproto-dpif-xlate.c
@@ -2510,7 +2510,8 @@ compose_output_action__(struct xlate_ctx *ctx, ofp_port_t 
ofp_port,
     if (out_port != ODPP_NONE) {
         ctx->xout->slow |= commit_odp_actions(flow, &ctx->base_flow,
                                               &ctx->xout->odp_actions,
-                                              &ctx->xout->wc);
+                                              &ctx->xout->wc,
+                                              ctx->xbridge->masked_set_action);
 
         if (ctx->use_recirc) {
             struct ovs_action_hash *act_hash;
@@ -2890,7 +2891,8 @@ execute_controller_action(struct xlate_ctx *ctx, int len,
 
     ctx->xout->slow |= commit_odp_actions(&ctx->xin->flow, &ctx->base_flow,
                                           &ctx->xout->odp_actions,
-                                          &ctx->xout->wc);
+                                          &ctx->xout->wc,
+                                          ctx->xbridge->masked_set_action);
 
     odp_execute_actions(NULL, &packet, 1, false, &md,
                         ofpbuf_data(&ctx->xout->odp_actions),
@@ -2986,7 +2988,8 @@ compose_recirculate_action(struct xlate_ctx *ctx,
 
     ctx->xout->slow |= commit_odp_actions(&ctx->xin->flow, &ctx->base_flow,
                                           &ctx->xout->odp_actions,
-                                          &ctx->xout->wc);
+                                          &ctx->xout->wc,
+                                          ctx->xbridge->masked_set_action);
     nl_msg_put_u32(&ctx->xout->odp_actions, OVS_ACTION_ATTR_RECIRC, id);
 }
 
@@ -3003,7 +3006,8 @@ compose_mpls_push_action(struct xlate_ctx *ctx, struct 
ofpact_push_mpls *mpls)
     if (!n) {
         ctx->xout->slow |= commit_odp_actions(flow, &ctx->base_flow,
                                               &ctx->xout->odp_actions,
-                                              &ctx->xout->wc);
+                                              &ctx->xout->wc,
+                                              ctx->xbridge->masked_set_action);
     } else if (n >= FLOW_MAX_MPLS_LABELS) {
         if (ctx->xin->packet != NULL) {
             static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
@@ -3363,7 +3367,8 @@ xlate_sample_action(struct xlate_ctx *ctx,
 
   ctx->xout->slow |= commit_odp_actions(&ctx->xin->flow, &ctx->base_flow,
                                         &ctx->xout->odp_actions,
-                                        &ctx->xout->wc);
+                                        &ctx->xout->wc,
+                                        ctx->xbridge->masked_set_action);
 
   compose_flow_sample_cookie(os->probability, os->collector_set_id,
                              os->obs_domain_id, os->obs_point_id, &cookie);
diff --git a/tests/ofproto-dpif.at b/tests/ofproto-dpif.at
index 0253cb0..f1f786f 100644
--- a/tests/ofproto-dpif.at
+++ b/tests/ofproto-dpif.at
@@ -192,8 +192,8 @@ table=2 ip actions=set_field:192.168.3.91->ip_src,output(11)
 AT_CHECK([ovs-ofctl -O OpenFlow12 add-flows br0 flows.txt])
 AT_CHECK([ovs-appctl ofproto/trace br0 
'in_port=1,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:07,dl_type=0x0800,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_proto=1,nw_tos=0,nw_ttl=128,icmp_type=8,icmp_code=0'],
 [0], [stdout])
 AT_CHECK([tail -2 stdout], [0],
-  [Megaflow: 
recirc_id=0,skb_priority=0,icmp,in_port=1,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=128
-Datapath actions: 
10,set(ipv4(src=192.168.3.91,dst=192.168.0.2,proto=1,tos=0,ttl=128,frag=no)),11,set(ipv4(src=192.168.3.90,dst=192.168.0.2,proto=1,tos=0,ttl=128,frag=no)),13
+  [Megaflow: 
recirc_id=0,skb_priority=0,ip,in_port=1,nw_src=192.168.0.1,nw_frag=no
+Datapath actions: 
10,set(ipv4(src=192.168.3.91/255.255.255.255,dst=0.0.0.0/0.0.0.0,proto=0/0,tos=0/0,ttl=0/0,frag=no/0)),11,set(ipv4(src=192.168.3.90/255.255.255.255,dst=0.0.0.0/0.0.0.0,proto=0/0,tos=0/0,ttl=0/0,frag=no/0)),13
 ])
 OVS_VSWITCHD_STOP
 AT_CLEANUP
@@ -208,8 +208,8 @@ table=1 tcp 
actions=set_field:91->tp_src,output(11),clear_actions
 AT_CHECK([ovs-ofctl -O OpenFlow12 add-flows br0 flows.txt])
 AT_CHECK([ovs-appctl ofproto/trace br0 
'in_port=1,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:07,dl_type=0x0800,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_proto=6,nw_tos=0,nw_ttl=128,tp_src=8,tp_dst=9'],
 [0], [stdout])
 AT_CHECK([tail -2 stdout], [0],
-  [Megaflow: 
recirc_id=0,skb_priority=0,tcp,in_port=1,nw_frag=no,tp_src=8,tp_dst=9
-Datapath actions: 10,set(tcp(src=91,dst=9)),11
+  [Megaflow: recirc_id=0,skb_priority=0,tcp,in_port=1,nw_frag=no,tp_src=8
+Datapath actions: 10,set(tcp(src=91/0xffff,dst=0/0)),11
 ])
 OVS_VSWITCHD_STOP
 AT_CLEANUP
@@ -231,9 +231,11 @@ ADD_OF_PORTS([br0], [1], [10], [11])
 AT_CHECK([ovs-ofctl -O OpenFlow12 add-group br0 
'group_id=1234,type=all,bucket=output:10,set_field:192.168.3.90->ip_src,bucket=output:11'])
 AT_CHECK([ovs-ofctl -O OpenFlow12 add-flow br0 'ip actions=group:1234'])
 AT_CHECK([ovs-appctl ofproto/trace br0 
'in_port=1,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:07,dl_type=0x0800,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_proto=1,nw_tos=0,nw_ttl=128,icmp_type=8,icmp_code=0'],
 [0], [stdout])
+# Must match on the source address to be able to restore it's value for
+# the second bucket
 AT_CHECK([tail -2 stdout], [0],
-  [Megaflow: 
recirc_id=0,skb_priority=0,icmp,in_port=1,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=128
-Datapath actions: 
set(ipv4(src=192.168.3.90,dst=192.168.0.2,proto=1,tos=0,ttl=128,frag=no)),10,set(ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=128,frag=no)),11
+  [Megaflow: 
recirc_id=0,skb_priority=0,ip,in_port=1,nw_src=192.168.0.1,nw_frag=no
+Datapath actions: 
set(ipv4(src=192.168.3.90/255.255.255.255,dst=0.0.0.0/0.0.0.0,proto=0/0,tos=0/0,ttl=0/0,frag=no/0)),10,set(ipv4(src=192.168.0.1/255.255.255.255,dst=0.0.0.0/0.0.0.0,proto=0/0,tos=0/0,ttl=0/0,frag=no/0)),11
 ])
 OVS_VSWITCHD_STOP
 AT_CLEANUP
@@ -256,9 +258,11 @@ ADD_OF_PORTS([br0], [1], [10], [11])
 AT_CHECK([ovs-ofctl -O OpenFlow12 add-group br0 
'group_id=1234,type=all,bucket=output:10,set_field:192.168.3.90->ip_src,bucket=output:11'])
 AT_CHECK([ovs-ofctl -O OpenFlow12 add-flow br0 'ip 
actions=write_actions(group:1234)'])
 AT_CHECK([ovs-appctl ofproto/trace br0 
'in_port=1,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:07,dl_type=0x0800,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_proto=1,nw_tos=0,nw_ttl=128,icmp_type=8,icmp_code=0'],
 [0], [stdout])
+# Must match on the source address to be able to restore it's value for
+# the third bucket
 AT_CHECK([tail -2 stdout], [0],
-  [Megaflow: 
recirc_id=0,skb_priority=0,icmp,in_port=1,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=128
-Datapath actions: 
set(ipv4(src=192.168.3.90,dst=192.168.0.2,proto=1,tos=0,ttl=128,frag=no)),10,set(ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=128,frag=no)),11
+  [Megaflow: 
recirc_id=0,skb_priority=0,ip,in_port=1,nw_src=192.168.0.1,nw_frag=no
+Datapath actions: 
set(ipv4(src=192.168.3.90/255.255.255.255,dst=0.0.0.0/0.0.0.0,proto=0/0,tos=0/0,ttl=0/0,frag=no/0)),10,set(ipv4(src=192.168.0.1/255.255.255.255,dst=0.0.0.0/0.0.0.0,proto=0/0,tos=0/0,ttl=0/0,frag=no/0)),11
 ])
 OVS_VSWITCHD_STOP
 AT_CLEANUP
@@ -481,20 +485,20 @@ table=1 in_port=1 action=dec_ttl,output:3
 AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
 AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 
'in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=111,tos=0,ttl=2,frag=no)'
 -generate], [0], [stdout])
 AT_CHECK([tail -4 stdout], [0],
-  [Megaflow: 
recirc_id=0,skb_priority=0,ip,in_port=1,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_proto=111,nw_tos=0,nw_ecn=0,nw_ttl=2
-Datapath actions: 
set(ipv4(src=192.168.0.1,dst=192.168.0.2,proto=111,tos=0,ttl=1,frag=no)),2,4
+  [Megaflow: recirc_id=0,skb_priority=0,ip,in_port=1,nw_ttl=2,nw_frag=no
+Datapath actions: 
set(ipv4(src=0.0.0.0/0.0.0.0,dst=0.0.0.0/0.0.0.0,proto=0/0,tos=0/0,ttl=1/0xff,frag=no/0)),2,4
 This flow is handled by the userspace slow path because it:
        - Sends "packet-in" messages to the OpenFlow controller.
 ])
 AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 
'in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=111,tos=0,ttl=3,frag=no)'],
 [0], [stdout])
 AT_CHECK([tail -2 stdout], [0],
-  [Megaflow: 
recirc_id=0,skb_priority=0,ip,in_port=1,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_proto=111,nw_tos=0,nw_ecn=0,nw_ttl=3
-Datapath actions: 
set(ipv4(src=192.168.0.1,dst=192.168.0.2,proto=111,tos=0,ttl=2,frag=no)),2,set(ipv4(src=192.168.0.1,dst=192.168.0.2,proto=111,tos=0,ttl=1,frag=no)),3,4
+  [Megaflow: recirc_id=0,skb_priority=0,ip,in_port=1,nw_ttl=3,nw_frag=no
+Datapath actions: 
set(ipv4(src=0.0.0.0/0.0.0.0,dst=0.0.0.0/0.0.0.0,proto=0/0,tos=0/0,ttl=2/0xff,frag=no/0)),2,set(ipv4(src=0.0.0.0/0.0.0.0,dst=0.0.0.0/0.0.0.0,proto=0/0,tos=0/0,ttl=1/0xff,frag=no/0)),3,4
 ])
 AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 
'in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x86dd),ipv6(src=::1,dst=::2,label=0,proto=10,tclass=0x70,hlimit=128,frag=no)'],
 [0], [stdout])
 AT_CHECK([tail -2 stdout], [0],
-  [Megaflow: 
recirc_id=0,skb_priority=0,ipv6,in_port=1,ipv6_src=::1,ipv6_dst=::2,ipv6_label=0x00000,nw_proto=10,nw_tos=112,nw_ecn=0,nw_ttl=128
-Datapath actions: 
set(ipv6(src=::1,dst=::2,label=0,proto=10,tclass=0x70,hlimit=127,frag=no)),2,set(ipv6(src=::1,dst=::2,label=0,proto=10,tclass=0x70,hlimit=126,frag=no)),3,4
+  [Megaflow: recirc_id=0,skb_priority=0,ipv6,in_port=1,nw_ttl=128,nw_frag=no
+Datapath actions: 
set(ipv6(src=::/::,dst=::/::,label=0/0,proto=0/0,tclass=0/0,hlimit=127/0xff,frag=no/0)),2,set(ipv6(src=::/::,dst=::/::,label=0/0,proto=0/0,tclass=0/0,hlimit=126/0xff,frag=no/0)),3,4
 ])
 
 AT_CAPTURE_FILE([ofctl_monitor.log])
@@ -591,16 +595,15 @@ AT_CHECK([ovs-vsctl -- \
         --id=@q2 create Queue dscp=2], [0], [ignore])
 AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 
'in_port(9),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=1.1.1.1,dst=2.2.2.2,proto=1,tos=0xff,ttl=128,frag=no),icmp(type=8,code=0)'],
 [0], [stdout])
 AT_CHECK([tail -2 stdout], [0],
-  [Megaflow: 
recirc_id=0,skb_priority=0,icmp,in_port=9,nw_src=1.1.1.1,nw_dst=2.2.2.2,nw_tos=252,nw_ecn=3,nw_ttl=128
+  [Megaflow: recirc_id=0,skb_priority=0,ip,in_port=9,nw_tos=252,nw_frag=no
 Datapath actions: dnl
 100,dnl
-set(ipv4(src=1.1.1.1,dst=2.2.2.2,proto=1,tos=0x7,ttl=128,frag=no)),set(skb_priority(0x1)),1,dnl
-set(ipv4(src=1.1.1.1,dst=2.2.2.2,proto=1,tos=0xb,ttl=128,frag=no)),set(skb_priority(0x2)),1,dnl
+set(ipv4(src=0.0.0.0/0.0.0.0,dst=0.0.0.0/0.0.0.0,proto=0/0,tos=0x4/0xfc,ttl=0/0,frag=no/0)),set(skb_priority(0x1)),1,dnl
+set(ipv4(src=0.0.0.0/0.0.0.0,dst=0.0.0.0/0.0.0.0,proto=0/0,tos=0x8/0xfc,ttl=0/0,frag=no/0)),set(skb_priority(0x2)),1,dnl
 1,dnl
-set(ipv4(src=1.1.1.1,dst=2.2.2.2,proto=1,tos=0x7,ttl=128,frag=no)),set(skb_priority(0x1)),1,dnl
-set(ipv4(src=1.1.1.1,dst=2.2.2.2,proto=1,tos=0xff,ttl=128,frag=no)),set(skb_priority(0)),1,dnl
-set(ipv4(src=1.1.1.1,dst=2.2.2.2,proto=1,tos=0x3,ttl=128,frag=no)),1,dnl
-100
+set(ipv4(src=0.0.0.0/0.0.0.0,dst=0.0.0.0/0.0.0.0,proto=0/0,tos=0x4/0xfc,ttl=0/0,frag=no/0)),set(skb_priority(0x1)),1,dnl
+set(ipv4(src=0.0.0.0/0.0.0.0,dst=0.0.0.0/0.0.0.0,proto=0/0,tos=0xfc/0xfc,ttl=0/0,frag=no/0)),set(skb_priority(0)),1,dnl
+set(ipv4(src=0.0.0.0/0.0.0.0,dst=0.0.0.0/0.0.0.0,proto=0/0,tos=0/0xfc,ttl=0/0,frag=no/0)),1,100
 ])
 OVS_VSWITCHD_STOP
 AT_CLEANUP
@@ -5231,7 +5234,7 @@ AT_CHECK([ovs-appctl netdev-dummy/receive p1 
'in_port(1),eth(src=50:54:00:00:00:
 sleep 1
 AT_CHECK([cat ovs-vswitchd.log | FILTER_FLOW_INSTALL | STRIP_XOUT], [0], [dnl
 
skb_priority(0),skb_mark(0/0),recirc_id(0),dp_hash(0/0),in_port(1),eth(src=50:54:00:00:00:09/00:00:00:00:00:00,dst=50:54:00:00:00:0a/00:00:00:00:00:00),eth_type(0x0800),ipv4(src=10.0.0.2/0.0.0.2,dst=10.0.0.1/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no/0xff),icmp(type=8/0,code=0/0),
 actions: <del>
-skb_priority(0),skb_mark(0/0),recirc_id(0),dp_hash(0/0),in_port(1),eth(src=50:54:00:00:00:0b/00:00:00:00:00:00,dst=50:54:00:00:00:0c/00:00:00:00:00:00),eth_type(0x0800),ipv4(src=10.0.0.4,dst=10.0.0.3,proto=1,tos=0,ttl=64,frag=no),icmp(type=8/0,code=0/0),
 actions: <del>
+skb_priority(0),skb_mark(0/0),recirc_id(0),dp_hash(0/0),in_port(1),eth(src=50:54:00:00:00:0b/00:00:00:00:00:00,dst=50:54:00:00:00:0c/00:00:00:00:00:00),eth_type(0x0800),ipv4(src=10.0.0.4/255.255.255.255,dst=10.0.0.3/0.0.0.0,proto=1/0xff,tos=0/0,ttl=64/0xff,frag=no/0xff),icmp(type=8/0,code=0/0),
 actions: <del>
 ])
 OVS_VSWITCHD_STOP
 AT_CLEANUP
@@ -5247,19 +5250,12 @@ AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
 AT_CHECK([ovs-appctl netdev-dummy/receive p1 
'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)'])
 AT_CHECK([ovs-appctl netdev-dummy/receive p1 
'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.4,dst=10.0.0.3,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)'])
 sleep 1
-dnl The megaflows do not match the same fields, since the first packet
-dnl is essentially a no-op.  (The new destination MAC is the same as the
-dnl original.) The ofproto-dpif library un-wildcards the destination MAC
-dnl so that a packet that doesn't need its MAC address changed doesn't
-dnl hide one that does.  Since the first entry doesn't need to change,
-dnl only the destination MAC address is matched (as decided by
-dnl ofproto-dpif).  The second entry actually updates the destination
-dnl MAC, so both the source and destination MAC addresses are
-dnl un-wildcarded, since the ODP commit functions update both the source
-dnl and destination MAC addresses.
+dnl The first packet is essentially a no-op, as the new destination MAC is the
+dnl same as the original.  The second entry actually updates the destination
+dnl MAC.
 AT_CHECK([cat ovs-vswitchd.log | FILTER_FLOW_INSTALL | STRIP_USED], [0], [dnl
 
skb_priority(0),skb_mark(0/0),recirc_id(0),dp_hash(0/0),in_port(1),eth(src=50:54:00:00:00:09/00:00:00:00:00:00,dst=50:54:00:00:00:0a/ff:ff:ff:ff:ff:ff),eth_type(0x0800),ipv4(src=10.0.0.2/0.0.0.0,dst=10.0.0.1/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no/0xff),icmp(type=8/0,code=0/0),
 actions:2
-skb_priority(0),skb_mark(0/0),recirc_id(0),dp_hash(0/0),in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.4/0.0.0.0,dst=10.0.0.3/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no/0xff),icmp(type=8/0,code=0/0),
 actions:set(eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0a)),2
+skb_priority(0),skb_mark(0/0),recirc_id(0),dp_hash(0/0),in_port(1),eth(src=50:54:00:00:00:0b/00:00:00:00:00:00,dst=50:54:00:00:00:0c/ff:ff:ff:ff:ff:ff),eth_type(0x0800),ipv4(src=10.0.0.4/0.0.0.0,dst=10.0.0.3/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no/0xff),icmp(type=8/0,code=0/0),
 
actions:set(eth(src=00:00:00:00:00:00/00:00:00:00:00:00,dst=50:54:00:00:00:0a/ff:ff:ff:ff:ff:ff)),2
 ])
 OVS_VSWITCHD_STOP
 AT_CLEANUP
diff --git a/tests/tunnel.at b/tests/tunnel.at
index 2ae8179..e80f9b0 100644
--- a/tests/tunnel.at
+++ b/tests/tunnel.at
@@ -88,15 +88,15 @@ Datapath actions: 2
 dnl Tunnel CE and encapsulated packet ECT(1)
 AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 
'tunnel(tun_id=0x0,src=1.1.1.1,dst=2.2.2.2,tos=0x3,ttl=64,flags()),in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=1,ttl=64,frag=no),tcp(src=8,dst=9)'],
 [0], [stdout])
 AT_CHECK([tail -2 stdout], [0],
-  [Megaflow: 
pkt_mark=0,recirc_id=0,skb_priority=0,tcp,tun_id=0,tun_src=1.1.1.1,tun_dst=2.2.2.2,tun_tos=3,tun_ttl=64,,in_port=1,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=1,nw_ttl=64
-Datapath actions: 
set(ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0x3,ttl=64,frag=no)),2
+  [Megaflow: 
pkt_mark=0,recirc_id=0,skb_priority=0,ip,tun_id=0,tun_src=1.1.1.1,tun_dst=2.2.2.2,tun_tos=3,tun_ttl=64,,in_port=1,nw_ecn=1,nw_frag=no
+Datapath actions: 
set(ipv4(src=0.0.0.0/0.0.0.0,dst=0.0.0.0/0.0.0.0,proto=0/0,tos=0x3/0x3,ttl=0/0,frag=no/0)),2
 ])
 
 dnl Tunnel CE and encapsulated packet ECT(2)
 AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 
'tunnel(tun_id=0x0,src=1.1.1.1,dst=2.2.2.2,tos=0x3,ttl=64,flags()),in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=2,ttl=64,frag=no),tcp(src=8,dst=9)'],
 [0], [stdout])
 AT_CHECK([tail -2 stdout], [0],
-  [Megaflow: 
pkt_mark=0,recirc_id=0,skb_priority=0,tcp,tun_id=0,tun_src=1.1.1.1,tun_dst=2.2.2.2,tun_tos=3,tun_ttl=64,,in_port=1,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=2,nw_ttl=64
-Datapath actions: 
set(ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0x3,ttl=64,frag=no)),2
+  [Megaflow: 
pkt_mark=0,recirc_id=0,skb_priority=0,ip,tun_id=0,tun_src=1.1.1.1,tun_dst=2.2.2.2,tun_tos=3,tun_ttl=64,,in_port=1,nw_ecn=2,nw_frag=no
+Datapath actions: 
set(ipv4(src=0.0.0.0/0.0.0.0,dst=0.0.0.0/0.0.0.0,proto=0/0,tos=0x3/0x3,ttl=0/0,frag=no/0)),2
 ])
 
 dnl Tunnel CE and encapsulated packet Non-ECT
-- 
1.7.10.4

_______________________________________________
dev mailing list
dev@openvswitch.org
http://openvswitch.org/mailman/listinfo/dev

Reply via email to