Almost all current actions can be expressed in the form of
push/pop/set <field>, where field is one of the match fields. We can
create three base actions and take a field. This has both a nice
symmetry and avoids inconsistencies where we can match on the vlan
TPID but not set it.
Following patch converts all actions to this new format.

Bug #7115
---
 include/openvswitch/datapath-protocol.h |   38 +++---
 lib/dpif-linux.c                        |    4 +-
 lib/dpif-netdev.c                       |  218 +++++++++++++++------------
 lib/dpif.c                              |   16 ++-
 lib/odp-util.c                          |  247 ++++++++++++++++++++++--------
 lib/odp-util.h                          |   10 +-
 ofproto/in-band.c                       |    3 +-
 ofproto/ofproto-dpif.c                  |  226 ++++++++++++++++++++---------
 utilities/ovs-dpctl.c                   |   10 +-
 9 files changed, 515 insertions(+), 257 deletions(-)

diff --git a/include/openvswitch/datapath-protocol.h 
b/include/openvswitch/datapath-protocol.h
index 6c89411..071f02a 100644
--- a/include/openvswitch/datapath-protocol.h
+++ b/include/openvswitch/datapath-protocol.h
@@ -288,6 +288,7 @@ struct ovs_flow_stats {
 
 enum ovs_key_type {
        OVS_KEY_ATTR_UNSPEC,
+       OVS_KEY_ATTR_NONE,
        OVS_KEY_ATTR_TUN_ID,    /* 64-bit tunnel ID */
        OVS_KEY_ATTR_IN_PORT,   /* 32-bit OVS dp port number */
        OVS_KEY_ATTR_ETHERNET,  /* struct ovs_key_ethernet */
@@ -427,28 +428,27 @@ enum ovs_sample_attr {
 
 #define OVS_SAMPLE_ATTR_MAX (__OVS_SAMPLE_ATTR_MAX - 1)
 
-/* Action types. */
+/* Base action types. */
 enum ovs_action_type {
        OVS_ACTION_ATTR_UNSPEC,
-       OVS_ACTION_ATTR_OUTPUT,       /* Output to switch port. */
-       OVS_ACTION_ATTR_USERSPACE,    /* Send copy to userspace. */
-       OVS_ACTION_ATTR_PUSH_VLAN,    /* Set the 802.1q TCI value. */
-       OVS_ACTION_ATTR_POP_VLAN,     /* Strip the 802.1q header. */
-       OVS_ACTION_ATTR_SET_DL_SRC,   /* Ethernet source address. */
-       OVS_ACTION_ATTR_SET_DL_DST,   /* Ethernet destination address. */
-       OVS_ACTION_ATTR_SET_NW_SRC,   /* IPv4 source address. */
-       OVS_ACTION_ATTR_SET_NW_DST,   /* IPv4 destination address. */
-       OVS_ACTION_ATTR_SET_NW_TOS,   /* IP ToS/DSCP field (6 bits). */
-       OVS_ACTION_ATTR_SET_TP_SRC,   /* TCP/UDP source port. */
-       OVS_ACTION_ATTR_SET_TP_DST,   /* TCP/UDP destination port. */
-       OVS_ACTION_ATTR_SET_TUNNEL,   /* Set the encapsulating tunnel ID. */
-       OVS_ACTION_ATTR_SET_PRIORITY, /* Set skb->priority. */
-       OVS_ACTION_ATTR_POP_PRIORITY, /* Restore original skb->priority. */
-       OVS_ACTION_ATTR_SAMPLE,       /* Execute list of actions at given
-                                        probability. */
-       __OVS_ACTION_ATTR_MAX
+       OVS_ACTION_ATTR_OUTPUT,                 /* Output to switch port. */
+       OVS_ACTION_ATTR_USERSPACE,              /* Send copy to userspace. */
+       OVS_ACTION_ATTR_PUSH,                   /* push pakcet header. */
+       OVS_ACTION_ATTR_POP,                    /* Pop packet headerheader. */
+       OVS_ACTION_ATTR_SET,                    /* Set packet field(s). */
+       OVS_ACTION_ATTR_SET_PRIORITY,           /* Set skb->priority. */
+       OVS_ACTION_ATTR_RESTORE_PRIORITY,       /* Restore skb->priority. */
+       OVS_ACTION_ATTR_SAMPLE,                 /* Sample action */
 };
 
-#define OVS_ACTION_ATTR_MAX (__OVS_ACTION_ATTR_MAX - 1)
+/**
+ * OVS ACTIONS are expressed using enum ovs_action_type and enum ovs_key_type.
+ * enum ovs_action_type is like base action and ovs_key_type specifies which
+ * field to act on. e.g. push/pop/set <field>.
+ * This has both a nice symmetry and avoids inconsistencies where we can match
+ * on a key but could not set it.
+ */
+
+#define OVS_ACT_TYPE(type, key_id)     ((type << 8) | key_id)
 
 #endif  /* openvswitch/datapath-protocol.h */
diff --git a/lib/dpif-linux.c b/lib/dpif-linux.c
index 8981500..dcd5239 100644
--- a/lib/dpif-linux.c
+++ b/lib/dpif-linux.c
@@ -1234,6 +1234,7 @@ dpif_linux_vport_send(int dp_ifindex, uint32_t port_no,
     struct odputil_keybuf keybuf;
     struct flow flow;
     uint64_t action;
+    uint16_t type;
 
     ofpbuf_use_const(&packet, data, size);
     flow_extract(&packet, htonll(0), 0, &flow);
@@ -1242,7 +1243,8 @@ dpif_linux_vport_send(int dp_ifindex, uint32_t port_no,
     odp_flow_key_from_flow(&key, &flow);
 
     ofpbuf_use_stack(&actions, &action, sizeof action);
-    nl_msg_put_u32(&actions, OVS_ACTION_ATTR_OUTPUT, port_no);
+    type = OVS_ACT_TYPE(OVS_ACTION_ATTR_OUTPUT, OVS_KEY_ATTR_NONE);
+    nl_msg_put_u32(&actions, type, port_no);
 
     return dpif_linux_execute__(dp_ifindex, 0, key.data, key.size,
                                 actions.data, actions.size, &packet);
diff --git a/lib/dpif-netdev.c b/lib/dpif-netdev.c
index af62bf0..b5b805d 100644
--- a/lib/dpif-netdev.c
+++ b/lib/dpif-netdev.c
@@ -702,48 +702,48 @@ dpif_netdev_validate_actions(const struct nlattr *actions,
     NL_ATTR_FOR_EACH (a, left, actions, actions_len) {
         uint16_t type = nl_attr_type(a);
         int len = odp_action_len(type);
+        const struct ovs_key_8021q *q_key;
+        const struct ovs_key_ipv4 *ipv4_key;
 
         if (len != nl_attr_get_size(a)) {
             return EINVAL;
         }
-
         switch (type) {
-        case OVS_ACTION_ATTR_OUTPUT:
+        case OVS_ACT_TYPE(OVS_ACTION_ATTR_OUTPUT, OVS_KEY_ATTR_NONE):
             if (nl_attr_get_u32(a) >= MAX_PORTS) {
                 return EINVAL;
             }
             break;
 
-        case OVS_ACTION_ATTR_USERSPACE:
+        case OVS_ACT_TYPE(OVS_ACTION_ATTR_USERSPACE, OVS_KEY_ATTR_NONE):
             break;
 
-        case OVS_ACTION_ATTR_PUSH_VLAN:
+        case OVS_ACT_TYPE(OVS_ACTION_ATTR_PUSH, OVS_KEY_ATTR_8021Q):
             *mutates = true;
-            if (nl_attr_get_be16(a) & htons(VLAN_CFI)) {
+            q_key = nl_attr_get_unspec(a, sizeof(*q_key));
+            if (!q_key || q_key->q_tci & htons(VLAN_CFI)) {
                 return EINVAL;
             }
             break;
 
-        case OVS_ACTION_ATTR_SET_NW_TOS:
+        case OVS_ACT_TYPE(OVS_ACTION_ATTR_SET, OVS_KEY_ATTR_IPV4):
             *mutates = true;
-            if (nl_attr_get_u8(a) & IP_ECN_MASK) {
+            ipv4_key = nl_attr_get_unspec(a, sizeof (*ipv4_key));
+            if (ipv4_key->ipv4_tos & IP_ECN_MASK) {
                 return EINVAL;
             }
             break;
 
-        case OVS_ACTION_ATTR_POP_VLAN:
-        case OVS_ACTION_ATTR_SET_DL_SRC:
-        case OVS_ACTION_ATTR_SET_DL_DST:
-        case OVS_ACTION_ATTR_SET_NW_SRC:
-        case OVS_ACTION_ATTR_SET_NW_DST:
-        case OVS_ACTION_ATTR_SET_TP_SRC:
-        case OVS_ACTION_ATTR_SET_TP_DST:
+        case OVS_ACT_TYPE(OVS_ACTION_ATTR_POP, OVS_KEY_ATTR_8021Q):
+        case OVS_ACT_TYPE(OVS_ACTION_ATTR_SET, OVS_KEY_ATTR_ETHERNET):
+        case OVS_ACT_TYPE(OVS_ACTION_ATTR_SET, OVS_KEY_ATTR_TCP):
+        case OVS_ACT_TYPE(OVS_ACTION_ATTR_SET, OVS_KEY_ATTR_UDP):
             *mutates = true;
             break;
 
-        case OVS_ACTION_ATTR_SET_TUNNEL:
-        case OVS_ACTION_ATTR_SET_PRIORITY:
-        case OVS_ACTION_ATTR_POP_PRIORITY:
+        case OVS_ACT_TYPE(OVS_ACTION_ATTR_SET, OVS_KEY_ATTR_TUN_ID):
+        case OVS_ACT_TYPE(OVS_ACTION_ATTR_SET_PRIORITY, OVS_KEY_ATTR_NONE):
+        case OVS_ACT_TYPE(OVS_ACTION_ATTR_RESTORE_PRIORITY, OVS_KEY_ATTR_NONE):
         default:
             return EOPNOTSUPP;
         }
@@ -1159,93 +1159,114 @@ dp_netdev_pop_vlan(struct ofpbuf *packet)
 }
 
 static void
-dp_netdev_set_dl_src(struct ofpbuf *packet, const uint8_t 
dl_addr[ETH_ADDR_LEN])
+dp_netdev_set_dl(struct ofpbuf *packet, const struct ovs_key_ethernet *eth_key)
 {
     struct eth_header *eh = packet->l2;
-    memcpy(eh->eth_src, dl_addr, sizeof eh->eth_src);
+    if (!eth_addr_equals(eth_key->eth_src, eh->eth_src)) {
+        memcpy(eh->eth_src, eth_key->eth_src, sizeof eh->eth_src);
+    }
+    if (!eth_addr_equals(eth_key->eth_dst, eh->eth_dst)) {
+        memcpy(eh->eth_dst, eth_key->eth_dst, sizeof eh->eth_dst);
+    }
+}
+
+static bool
+is_ip(const struct ofpbuf *packet, const struct flow *flow)
+{
+    return flow->dl_type == htons(ETH_TYPE_IP) && packet->l4;
 }
 
 static void
-dp_netdev_set_dl_dst(struct ofpbuf *packet, const uint8_t 
dl_addr[ETH_ADDR_LEN])
+dp_netdev_set_ip_addr(struct ofpbuf *packet, uint8_t nw_proto, ovs_be32 *addr,
+                      ovs_be32 new_addr)
 {
-    struct eth_header *eh = packet->l2;
-    memcpy(eh->eth_dst, dl_addr, sizeof eh->eth_dst);
+    struct ip_header *nh = packet->l3;
+
+    if (nw_proto == IPPROTO_TCP && packet->l7) {
+        struct tcp_header *th = packet->l4;
+        th->tcp_csum = recalc_csum32(th->tcp_csum, *addr, new_addr);
+    } else if (nw_proto == IPPROTO_UDP && packet->l7) {
+        struct udp_header *uh = packet->l4;
+        if (uh->udp_csum) {
+            uh->udp_csum = recalc_csum32(uh->udp_csum, *addr, new_addr);
+            if (!uh->udp_csum) {
+                uh->udp_csum = htons(0xffff);
+            }
+        }
+    }
+    nh->ip_csum = recalc_csum32(nh->ip_csum, *addr, new_addr);
+    *addr = new_addr;
 }
 
-static bool
-is_ip(const struct ofpbuf *packet, const struct flow *key)
+static void
+dp_netdev_set_ip_tos(struct ip_header *nh, uint8_t new_tos)
 {
-    return key->dl_type == htons(ETH_TYPE_IP) && packet->l4;
+    uint8_t *field = &nh->ip_tos;
+
+    /* Set the DSCP bits and preserve the ECN bits. */
+    uint8_t new = new_tos | (nh->ip_tos & IP_ECN_MASK);
+
+    nh->ip_csum = recalc_csum16(nh->ip_csum, htons((uint16_t)*field),
+                                   htons((uint16_t) new));
+    *field = new;
 }
 
 static void
-dp_netdev_set_nw_addr(struct ofpbuf *packet, const struct flow *key,
-                      const struct nlattr *a)
+dp_netdev_set_nw_addr(struct ofpbuf *packet, const struct flow *flow,
+                      const struct ovs_key_ipv4 *ipv4_key)
 {
-    if (is_ip(packet, key)) {
+    if (is_ip(packet, flow)) {
         struct ip_header *nh = packet->l3;
-        ovs_be32 ip = nl_attr_get_be32(a);
-        uint16_t type = nl_attr_type(a);
-        ovs_be32 *field;
-
-        field = type == OVS_ACTION_ATTR_SET_NW_SRC ? &nh->ip_src : &nh->ip_dst;
-        if (key->nw_proto == IPPROTO_TCP && packet->l7) {
-            struct tcp_header *th = packet->l4;
-            th->tcp_csum = recalc_csum32(th->tcp_csum, *field, ip);
-        } else if (key->nw_proto == IPPROTO_UDP && packet->l7) {
-            struct udp_header *uh = packet->l4;
-            if (uh->udp_csum) {
-                uh->udp_csum = recalc_csum32(uh->udp_csum, *field, ip);
-                if (!uh->udp_csum) {
-                    uh->udp_csum = htons(0xffff);
-                }
-            }
+
+        if (nh->ip_src != ipv4_key->ipv4_src) {
+            dp_netdev_set_ip_addr(packet, flow->nw_proto, &nh->ip_src,
+                                  ipv4_key->ipv4_src);
+        }
+        if (nh->ip_dst != ipv4_key->ipv4_dst) {
+            dp_netdev_set_ip_addr(packet, flow->nw_proto, &nh->ip_dst,
+                                  ipv4_key->ipv4_dst);
+        }
+        if (nh->ip_tos != ipv4_key->ipv4_tos) {
+            dp_netdev_set_ip_tos(nh, ipv4_key->ipv4_tos);
         }
-        nh->ip_csum = recalc_csum32(nh->ip_csum, *field, ip);
-        *field = ip;
     }
 }
 
 static void
-dp_netdev_set_nw_tos(struct ofpbuf *packet, const struct flow *key,
-                     uint8_t nw_tos)
+dp_netdev_set_port(ovs_be16 *port, ovs_be16 new_port, ovs_be16 *csum)
 {
-    if (is_ip(packet, key)) {
-        struct ip_header *nh = packet->l3;
-        uint8_t *field = &nh->ip_tos;
+    *csum = recalc_csum16(*csum, *port, new_port);
+    *port = new_port;
+}
 
-        /* Set the DSCP bits and preserve the ECN bits. */
-        uint8_t new = nw_tos | (nh->ip_tos & IP_ECN_MASK);
+static void
+dp_netdev_set_tcp_port(struct ofpbuf *packet, const struct flow *flow,
+                       const struct ovs_key_tcp *tcp_key)
+{
+    if (is_ip(packet, flow)) {
+        struct tcp_header *th = packet->l4;
 
-        nh->ip_csum = recalc_csum16(nh->ip_csum, htons((uint16_t)*field),
-                htons((uint16_t) new));
-        *field = new;
+        if (th->tcp_src != tcp_key->tcp_src) {
+            dp_netdev_set_port(&th->tcp_src, tcp_key->tcp_src, &th->tcp_csum);
+        }
+        if (th->tcp_dst != tcp_key->tcp_dst) {
+            dp_netdev_set_port(&th->tcp_dst, tcp_key->tcp_dst, &th->tcp_csum);
+        }
     }
 }
 
 static void
-dp_netdev_set_tp_port(struct ofpbuf *packet, const struct flow *key,
-                      const struct nlattr *a)
+dp_netdev_set_udp_port(struct ofpbuf *packet, const struct flow *flow,
+                       const struct ovs_key_udp *udp_key)
 {
-       if (is_ip(packet, key)) {
-        uint16_t type = nl_attr_type(a);
-        ovs_be16 port = nl_attr_get_be16(a);
-        ovs_be16 *field;
-
-        if (key->nw_proto == IPPROTO_TCP && packet->l7) {
-            struct tcp_header *th = packet->l4;
-            field = (type == OVS_ACTION_ATTR_SET_TP_SRC
-                     ? &th->tcp_src : &th->tcp_dst);
-            th->tcp_csum = recalc_csum16(th->tcp_csum, *field, port);
-            *field = port;
-        } else if (key->nw_proto == IPPROTO_UDP && packet->l7) {
-            struct udp_header *uh = packet->l4;
-            field = (type == OVS_ACTION_ATTR_SET_TP_SRC
-                     ? &uh->udp_src : &uh->udp_dst);
-            uh->udp_csum = recalc_csum16(uh->udp_csum, *field, port);
-            *field = port;
-        } else {
-            return;
+    if (is_ip(packet, flow)) {
+        struct udp_header *uh = packet->l4;
+
+        if (uh->udp_dst != udp_key->udp_src) {
+            dp_netdev_set_port(&uh->udp_src, udp_key->udp_src, &uh->udp_csum);
+        }
+        if (uh->udp_dst != udp_key->udp_dst) {
+            dp_netdev_set_port(&uh->udp_dst, udp_key->udp_dst, &uh->udp_csum);
         }
     }
 }
@@ -1303,45 +1324,52 @@ dp_netdev_execute_actions(struct dp_netdev *dp,
     unsigned int left;
 
     NL_ATTR_FOR_EACH_UNSAFE (a, left, actions, actions_len) {
+        const struct ovs_key_8021q *q_key;
+        const struct ovs_key_tcp *tcp_port_key;
+        const struct ovs_key_udp *udp_port_key;
+        const struct ovs_key_ipv4 *ipv4_key;
+        const struct ovs_key_ethernet *eth_key;
+
         switch (nl_attr_type(a)) {
-        case OVS_ACTION_ATTR_OUTPUT:
+        case OVS_ACT_TYPE(OVS_ACTION_ATTR_OUTPUT, OVS_KEY_ATTR_NONE):
             dp_netdev_output_port(dp, packet, nl_attr_get_u32(a));
             break;
 
-        case OVS_ACTION_ATTR_USERSPACE:
+        case OVS_ACT_TYPE(OVS_ACTION_ATTR_USERSPACE, OVS_KEY_ATTR_NONE):
             dp_netdev_output_userspace(dp, packet, DPIF_UC_ACTION,
                                      key, nl_attr_get_u64(a));
             break;
 
-        case OVS_ACTION_ATTR_PUSH_VLAN:
-            eth_push_vlan(packet, nl_attr_get_be16(a));
+        case OVS_ACT_TYPE(OVS_ACTION_ATTR_PUSH, OVS_KEY_ATTR_8021Q):
+            q_key = nl_attr_get_unspec(a, sizeof (*q_key));
+            eth_push_vlan(packet, q_key->q_tci);
             break;
 
-        case OVS_ACTION_ATTR_POP_VLAN:
+        case OVS_ACT_TYPE(OVS_ACTION_ATTR_POP, OVS_KEY_ATTR_8021Q):
             dp_netdev_pop_vlan(packet);
             break;
 
-        case OVS_ACTION_ATTR_SET_DL_SRC:
-            dp_netdev_set_dl_src(packet, nl_attr_get_unspec(a, ETH_ADDR_LEN));
-            break;
+        case OVS_ACT_TYPE(OVS_ACTION_ATTR_SET, OVS_KEY_ATTR_ETHERNET):
+            eth_key = nl_attr_get_unspec(a, sizeof (*eth_key));
 
-        case OVS_ACTION_ATTR_SET_DL_DST:
-            dp_netdev_set_dl_dst(packet, nl_attr_get_unspec(a, ETH_ADDR_LEN));
+            dp_netdev_set_dl(packet, eth_key);
             break;
 
-        case OVS_ACTION_ATTR_SET_NW_SRC:
-        case OVS_ACTION_ATTR_SET_NW_DST:
-            dp_netdev_set_nw_addr(packet, key, a);
+        case OVS_ACT_TYPE(OVS_ACTION_ATTR_SET, OVS_KEY_ATTR_IPV4):
+            ipv4_key = nl_attr_get_unspec(a, sizeof (*ipv4_key));
+            dp_netdev_set_nw_addr(packet, key, ipv4_key);
             break;
 
-        case OVS_ACTION_ATTR_SET_NW_TOS:
-            dp_netdev_set_nw_tos(packet, key, nl_attr_get_u8(a));
+        case OVS_ACT_TYPE(OVS_ACTION_ATTR_SET, OVS_KEY_ATTR_TCP):
+            tcp_port_key = nl_attr_get_unspec(a, sizeof (*tcp_port_key));
+            dp_netdev_set_tcp_port(packet, key, tcp_port_key);
             break;
 
-        case OVS_ACTION_ATTR_SET_TP_SRC:
-        case OVS_ACTION_ATTR_SET_TP_DST:
-            dp_netdev_set_tp_port(packet, key, a);
+         case OVS_ACT_TYPE(OVS_ACTION_ATTR_SET, OVS_KEY_ATTR_UDP):
+            udp_port_key = nl_attr_get_unspec(a, sizeof (*udp_port_key));
+            dp_netdev_set_udp_port(packet, key, udp_port_key);
             break;
+
         }
     }
     return 0;
diff --git a/lib/dpif.c b/lib/dpif.c
index c6940b1..7b99ba1 100644
--- a/lib/dpif.c
+++ b/lib/dpif.c
@@ -958,8 +958,15 @@ dpif_execute(struct dpif *dpif,
     if (!(error ? VLOG_DROP_WARN(&error_rl) : VLOG_DROP_DBG(&dpmsg_rl))) {
         struct ds ds = DS_EMPTY_INITIALIZER;
         char *packet = ofp_packet_to_string(buf->data, buf->size, buf->size);
+        struct flow flow;
+
         ds_put_format(&ds, "%s: execute ", dpif_name(dpif));
-        format_odp_actions(&ds, actions, actions_len);
+        if ((error = odp_flow_key_to_flow(key, key_len, &flow))) {
+            ds_put_cstr(&ds, "invalid key");
+            return error;
+        }
+
+        format_odp_actions(&ds, actions, actions_len, &flow);
         if (error) {
             ds_put_format(&ds, " failed (%s)", strerror(error));
         }
@@ -1188,8 +1195,13 @@ log_flow_message(const struct dpif *dpif, int error, 
const char *operation,
         dpif_flow_stats_format(stats, &ds);
     }
     if (actions || actions_len) {
+        struct flow flow;
         ds_put_cstr(&ds, ", actions:");
-        format_odp_actions(&ds, actions, actions_len);
+        if (odp_flow_key_to_flow(key, key_len, &flow)) {
+            ds_put_cstr(&ds, "invalid key");
+            return;
+        }
+        format_odp_actions(&ds, actions, actions_len, &flow);
     }
     vlog(THIS_MODULE, flow_message_log_level(error), "%s", ds_cstr(&ds));
     ds_destroy(&ds);
diff --git a/lib/odp-util.c b/lib/odp-util.c
index 7f5158f..d537d09 100644
--- a/lib/odp-util.c
+++ b/lib/odp-util.c
@@ -33,6 +33,11 @@
 #include "timeval.h"
 #include "util.h"
 
+static void
+format_odp_actions__(struct ds *ds,
+                     const struct nlattr *actions, size_t actions_len,
+                     struct flow *flow);
+
 /* The interface between userspace and kernel uses an "OVS_*" prefix.
  * Since this is fairly non-specific for the OVS userspace components,
  * "ODP_*" (Open vSwitch Datapath) is used as the prefix for
@@ -42,29 +47,31 @@
 int
 odp_action_len(uint16_t type)
 {
-    if (type > OVS_ACTION_ATTR_MAX) {
-        return -1;
-    }
-
-    switch ((enum ovs_action_type) type) {
-    case OVS_ACTION_ATTR_OUTPUT: return 4;
-    case OVS_ACTION_ATTR_USERSPACE: return 8;
-    case OVS_ACTION_ATTR_PUSH_VLAN: return 2;
-    case OVS_ACTION_ATTR_POP_VLAN: return 0;
-    case OVS_ACTION_ATTR_SET_DL_SRC: return ETH_ADDR_LEN;
-    case OVS_ACTION_ATTR_SET_DL_DST: return ETH_ADDR_LEN;
-    case OVS_ACTION_ATTR_SET_NW_SRC: return 4;
-    case OVS_ACTION_ATTR_SET_NW_DST: return 4;
-    case OVS_ACTION_ATTR_SET_NW_TOS: return 1;
-    case OVS_ACTION_ATTR_SET_TP_SRC: return 2;
-    case OVS_ACTION_ATTR_SET_TP_DST: return 2;
-    case OVS_ACTION_ATTR_SET_TUNNEL: return 8;
-    case OVS_ACTION_ATTR_SET_PRIORITY: return 4;
-    case OVS_ACTION_ATTR_POP_PRIORITY: return 0;
-
-    case OVS_ACTION_ATTR_SAMPLE:
-    case OVS_ACTION_ATTR_UNSPEC:
-    case __OVS_ACTION_ATTR_MAX:
+    switch (type) {
+    case OVS_ACT_TYPE(OVS_ACTION_ATTR_OUTPUT, OVS_KEY_ATTR_NONE):
+        return sizeof(uint32_t);
+    case OVS_ACT_TYPE(OVS_ACTION_ATTR_USERSPACE, OVS_KEY_ATTR_NONE):
+        return sizeof(uint64_t);
+    case OVS_ACT_TYPE(OVS_ACTION_ATTR_PUSH, OVS_KEY_ATTR_8021Q):
+        return sizeof(struct ovs_key_8021q);
+    case OVS_ACT_TYPE(OVS_ACTION_ATTR_POP, OVS_KEY_ATTR_8021Q):
+        return 0;
+    case OVS_ACT_TYPE(OVS_ACTION_ATTR_SET, OVS_KEY_ATTR_ETHERNET):
+        return sizeof(struct ovs_key_ethernet);
+    case OVS_ACT_TYPE(OVS_ACTION_ATTR_SET, OVS_KEY_ATTR_IPV4):
+        return sizeof(struct ovs_key_ipv4);
+    case OVS_ACT_TYPE(OVS_ACTION_ATTR_SET, OVS_KEY_ATTR_TCP):
+        return sizeof(struct ovs_key_tcp);
+    case OVS_ACT_TYPE(OVS_ACTION_ATTR_SET, OVS_KEY_ATTR_UDP):
+        return sizeof(struct ovs_key_udp);
+    case OVS_ACT_TYPE(OVS_ACTION_ATTR_SET, OVS_KEY_ATTR_TUN_ID):
+        return sizeof(ovs_be64);
+    case OVS_ACT_TYPE(OVS_ACTION_ATTR_SET_PRIORITY, OVS_KEY_ATTR_NONE):
+        return sizeof(uint32_t);
+    case OVS_ACT_TYPE(OVS_ACTION_ATTR_RESTORE_PRIORITY, OVS_KEY_ATTR_NONE):
+        return 0;
+    case OVS_ACT_TYPE(OVS_ACTION_ATTR_SAMPLE, OVS_KEY_ATTR_NONE):
+    case OVS_ACT_TYPE(OVS_ACTION_ATTR_UNSPEC, OVS_KEY_ATTR_NONE):
         return -1;
     }
 
@@ -91,7 +98,8 @@ format_generic_odp_action(struct ds *ds, const struct nlattr 
*a)
 }
 
 static void
-format_odp_sample_action(struct ds *ds, const struct nlattr *attr)
+format_odp_sample_action(struct ds *ds, const struct nlattr *attr,
+                         struct flow *flow)
 {
     static const struct nl_policy ovs_sample_policy[] = {
         [OVS_SAMPLE_ATTR_PROBABILITY] = { .type = NL_A_U32 },
@@ -117,20 +125,20 @@ format_odp_sample_action(struct ds *ds, const struct 
nlattr *attr)
     ds_put_cstr(ds, "actions(");
     nla_acts = nl_attr_get(a[OVS_SAMPLE_ATTR_ACTIONS]);
     len = nl_attr_get_size(a[OVS_SAMPLE_ATTR_ACTIONS]);
-    format_odp_actions(ds, nla_acts, len);
+    format_odp_actions__(ds, nla_acts, len, flow);
     ds_put_format(ds, "))");
 }
 
-void
-format_odp_action(struct ds *ds, const struct nlattr *a)
+static void
+format_odp_action(struct ds *ds, const struct nlattr *a,
+                  struct flow *flow)
 {
-    const uint8_t *eth;
-    ovs_be32 ip;
     struct user_action_cookie cookie;
     uint64_t data;
 
     if (nl_attr_get_size(a) != odp_action_len(nl_attr_type(a)) &&
-            nl_attr_type(a) != OVS_ACTION_ATTR_SAMPLE) {
+            nl_attr_type(a) != OVS_ACT_TYPE(OVS_ACTION_ATTR_SAMPLE,
+                                            OVS_KEY_ATTR_NONE)) {
         ds_put_format(ds, "bad length %zu, expected %d for: ",
                       nl_attr_get_size(a), odp_action_len(nl_attr_type(a)));
         format_generic_odp_action(ds, a);
@@ -138,10 +146,18 @@ format_odp_action(struct ds *ds, const struct nlattr *a)
     }
 
     switch (nl_attr_type(a)) {
-    case OVS_ACTION_ATTR_OUTPUT:
+    const struct ovs_key_8021q *q_key;
+    const struct ovs_key_tcp *tp_key;
+    const struct ovs_key_udp *up_key;
+    const struct ovs_key_ipv4 *ipv4_key;
+    const struct ovs_key_ethernet *eth_key;
+    bool action_set = false;
+
+    case OVS_ACT_TYPE(OVS_ACTION_ATTR_OUTPUT, OVS_KEY_ATTR_NONE):
         ds_put_format(ds, "%"PRIu16, nl_attr_get_u32(a));
         break;
-    case OVS_ACTION_ATTR_USERSPACE:
+
+    case OVS_ACT_TYPE(OVS_ACTION_ATTR_USERSPACE, OVS_KEY_ATTR_NONE):
         data = nl_attr_get_u64(a);
         memcpy(&cookie, &data, sizeof(cookie));
 
@@ -158,61 +174,147 @@ format_odp_action(struct ds *ds, const struct nlattr *a)
                           nl_attr_get_u64(a));
         }
         break;
-    case OVS_ACTION_ATTR_SET_TUNNEL:
+
+    case OVS_ACT_TYPE(OVS_ACTION_ATTR_SET, OVS_KEY_ATTR_TUN_ID):
         ds_put_format(ds, "set_tunnel(%#"PRIx64")",
                       ntohll(nl_attr_get_be64(a)));
         break;
-    case OVS_ACTION_ATTR_PUSH_VLAN:
+
+    case OVS_ACT_TYPE(OVS_ACTION_ATTR_PUSH, OVS_KEY_ATTR_8021Q):
+        q_key = nl_attr_get_unspec(a, sizeof (*q_key));
         ds_put_format(ds, "push_vlan(vid=%"PRIu16",pcp=%d)",
-                      vlan_tci_to_vid(nl_attr_get_be16(a)),
-                      vlan_tci_to_pcp(nl_attr_get_be16(a)));
+                      vlan_tci_to_vid(q_key->q_tci),
+                      vlan_tci_to_pcp(q_key->q_tci));
         break;
-    case OVS_ACTION_ATTR_POP_VLAN:
+
+    case OVS_ACT_TYPE(OVS_ACTION_ATTR_POP, OVS_KEY_ATTR_8021Q):
         ds_put_format(ds, "pop_vlan");
         break;
-    case OVS_ACTION_ATTR_SET_DL_SRC:
-        eth = nl_attr_get_unspec(a, ETH_ADDR_LEN);
-        ds_put_format(ds, "set_dl_src("ETH_ADDR_FMT")", ETH_ADDR_ARGS(eth));
-        break;
-    case OVS_ACTION_ATTR_SET_DL_DST:
-        eth = nl_attr_get_unspec(a, ETH_ADDR_LEN);
-        ds_put_format(ds, "set_dl_dst("ETH_ADDR_FMT")", ETH_ADDR_ARGS(eth));
-        break;
-    case OVS_ACTION_ATTR_SET_NW_SRC:
-        ip = nl_attr_get_be32(a);
-        ds_put_format(ds, "set_nw_src("IP_FMT")", IP_ARGS(&ip));
-        break;
-    case OVS_ACTION_ATTR_SET_NW_DST:
-        ip = nl_attr_get_be32(a);
-        ds_put_format(ds, "set_nw_dst("IP_FMT")", IP_ARGS(&ip));
+
+    case  OVS_ACT_TYPE(OVS_ACTION_ATTR_SET, OVS_KEY_ATTR_ETHERNET):
+        eth_key = nl_attr_get_unspec(a, sizeof (*eth_key));
+        if (!eth_addr_equals(flow->dl_src, eth_key->eth_src)) {
+            ds_put_format(ds, "set_dl_src("ETH_ADDR_FMT")",
+                           ETH_ADDR_ARGS(eth_key->eth_src));
+            memcpy(flow->dl_src, eth_key->eth_src, ETH_ADDR_LEN);
+            action_set = true;
+        }
+
+        if (!eth_addr_equals(flow->dl_dst, eth_key->eth_dst)) {
+            if (action_set) {
+                ds_put_format(ds, ",");
+            }
+            ds_put_format(ds, "set_dl_dst("ETH_ADDR_FMT")",
+                           ETH_ADDR_ARGS(eth_key->eth_dst));
+            memcpy(flow->dl_dst, eth_key->eth_dst, ETH_ADDR_LEN);
+            action_set = true;
+        }
+        if (!action_set) {
+            ds_put_format(ds, "set_dl(error)");
+        }
         break;
-    case OVS_ACTION_ATTR_SET_NW_TOS:
-        ds_put_format(ds, "set_nw_tos(%"PRIu8")", nl_attr_get_u8(a));
+
+    case OVS_ACT_TYPE(OVS_ACTION_ATTR_SET, OVS_KEY_ATTR_IPV4):
+        ipv4_key = nl_attr_get_unspec(a, sizeof (*ipv4_key));
+        if (flow->nw_src != ipv4_key->ipv4_src) {
+            ds_put_format(ds, "set_nw_src("IP_FMT")",
+                               IP_ARGS(&ipv4_key->ipv4_src));
+            flow->nw_src = ipv4_key->ipv4_src;
+            action_set = true;
+        }
+
+        if (flow->nw_dst != ipv4_key->ipv4_dst) {
+            if (action_set) {
+                ds_put_format(ds, ",");
+            }
+
+            ds_put_format(ds, "set_nw_dst("IP_FMT")",
+                               IP_ARGS(&ipv4_key->ipv4_dst));
+            flow->nw_dst = ipv4_key->ipv4_dst;
+            action_set = true;
+        }
+
+        if (flow->nw_tos != ipv4_key->ipv4_tos) {
+            if (action_set) {
+                ds_put_format(ds, ",");
+            }
+
+            ds_put_format(ds, "set_nw_tos(%"PRIu8")", ipv4_key->ipv4_tos);
+            flow->nw_tos = ipv4_key->ipv4_tos;
+            action_set = true;
+        }
+        if (!action_set) {
+            ds_put_format(ds, "set_nw(error)");
+        }
         break;
-    case OVS_ACTION_ATTR_SET_TP_SRC:
-        ds_put_format(ds, "set_tp_src(%"PRIu16")", ntohs(nl_attr_get_be16(a)));
+
+    case OVS_ACT_TYPE(OVS_ACTION_ATTR_SET, OVS_KEY_ATTR_TCP):
+        tp_key = nl_attr_get_unspec(a, sizeof (*tp_key));
+
+        if (flow->tp_src != tp_key->tcp_src) {
+            ds_put_format(ds, "set_tcp_src(%"PRIu16")", 
ntohs(tp_key->tcp_src));
+            flow->tp_src = tp_key->tcp_src;
+            action_set = true;
+        }
+
+        if (flow->tp_dst != tp_key->tcp_dst) {
+            if (action_set) {
+                ds_put_format(ds, ",");
+            }
+
+            ds_put_format(ds, "set_tcp_dst(%"PRIu16")", 
ntohs(tp_key->tcp_dst));
+            flow->tp_dst = tp_key->tcp_dst;
+            action_set = true;
+        }
+        if (!action_set) {
+            ds_put_format(ds, "set_tcp(error)");
+        }
         break;
-    case OVS_ACTION_ATTR_SET_TP_DST:
-        ds_put_format(ds, "set_tp_dst(%"PRIu16")", ntohs(nl_attr_get_be16(a)));
+
+    case OVS_ACT_TYPE(OVS_ACTION_ATTR_SET, OVS_KEY_ATTR_UDP):
+        up_key = nl_attr_get_unspec(a, sizeof (*up_key));
+        if (flow->tp_src != up_key->udp_src) {
+            ds_put_format(ds, "set_udp_src(%"PRIu16")", 
ntohs(up_key->udp_src));
+            flow->tp_src = up_key->udp_src;
+            action_set = true;
+        }
+
+        if (flow->tp_dst != up_key->udp_dst) {
+            if (action_set) {
+                ds_put_format(ds, ",");
+            }
+
+            ds_put_format(ds, "set_udp_dst(%"PRIu16")", 
ntohs(up_key->udp_dst));
+            flow->tp_dst = up_key->udp_dst;
+            action_set = true;
+        }
+        if (!action_set) {
+            ds_put_format(ds, "set_udp(error)");
+        }
         break;
-    case OVS_ACTION_ATTR_SET_PRIORITY:
+
+    case OVS_ACT_TYPE(OVS_ACTION_ATTR_SET_PRIORITY, OVS_KEY_ATTR_NONE):
         ds_put_format(ds, "set_priority(%#"PRIx32")", nl_attr_get_u32(a));
         break;
-    case OVS_ACTION_ATTR_POP_PRIORITY:
+
+    case OVS_ACT_TYPE(OVS_ACTION_ATTR_RESTORE_PRIORITY, OVS_KEY_ATTR_NONE):
         ds_put_cstr(ds, "pop_priority");
         break;
-    case OVS_ACTION_ATTR_SAMPLE:
-        format_odp_sample_action(ds, a);
+
+    case OVS_ACT_TYPE(OVS_ACTION_ATTR_SAMPLE, OVS_KEY_ATTR_NONE):
+        format_odp_sample_action(ds, a, flow);
         break;
+
     default:
         format_generic_odp_action(ds, a);
         break;
     }
 }
 
-void
-format_odp_actions(struct ds *ds, const struct nlattr *actions,
-                   size_t actions_len)
+static void
+format_odp_actions__(struct ds *ds,
+                     const struct nlattr *actions, size_t actions_len,
+                     struct flow *flow)
 {
     if (actions_len) {
         const struct nlattr *a;
@@ -222,7 +324,7 @@ format_odp_actions(struct ds *ds, const struct nlattr 
*actions,
             if (a != actions) {
                 ds_put_char(ds, ',');
             }
-            format_odp_action(ds, a);
+            format_odp_action(ds, a, flow);
         }
         if (left) {
             if (left == actions_len) {
@@ -234,6 +336,16 @@ format_odp_actions(struct ds *ds, const struct nlattr 
*actions,
         ds_put_cstr(ds, "drop");
     }
 }
+
+void
+format_odp_actions(struct ds *ds,
+                   const struct nlattr *actions, size_t actions_len,
+                   const struct flow *flow)
+{
+    struct flow temp_flow = *flow;
+    format_odp_actions__(ds, actions, actions_len, &temp_flow);
+}
+
 
 /* Returns the correct length of the payload for a flow key attribute of the
  * specified 'type', or -1 if 'type' is unknown. */
@@ -259,6 +371,7 @@ odp_flow_key_attr_len(uint16_t type)
     case OVS_KEY_ATTR_ARP: return sizeof(struct ovs_key_arp);
     case OVS_KEY_ATTR_ND: return sizeof(struct ovs_key_nd);
 
+    case OVS_KEY_ATTR_NONE:
     case OVS_KEY_ATTR_UNSPEC:
     case __OVS_KEY_ATTR_MAX:
         return -1;
@@ -1053,6 +1166,8 @@ odp_flow_key_to_flow(const struct nlattr *key, size_t 
key_len,
     switch (prev_type) {
     case OVS_KEY_ATTR_UNSPEC:
         return EINVAL;
+    case OVS_KEY_ATTR_NONE:
+        return EINVAL;
 
     case OVS_KEY_ATTR_TUN_ID:
     case OVS_KEY_ATTR_IN_PORT:
diff --git a/lib/odp-util.h b/lib/odp-util.h
index 0e4bc0b..bb0fd29 100644
--- a/lib/odp-util.h
+++ b/lib/odp-util.h
@@ -59,10 +59,10 @@ odp_port_to_ofp_port(uint16_t odp_port)
 }
 
 int odp_action_len(uint16_t type);
-void format_odp_action(struct ds *, const struct nlattr *);
-void format_odp_actions(struct ds *, const struct nlattr *odp_actions,
-                        size_t actions_len);
-
+void
+format_odp_actions(struct ds *ds,
+                   const struct nlattr *actions, size_t actions_len,
+                   const struct flow * flow);
 /* Upper bound on the length of a nlattr-formatted flow key.  The longest
  * nlattr-formatted flow key would be:
  *
@@ -84,7 +84,7 @@ void format_odp_actions(struct ds *, const struct nlattr 
*odp_actions,
 /* This is an imperfect sanity-check that ODPUTIL_FLOW_KEY_BYTES doesn't
  * need to be updated, but will at least raise awareness when new OVS
  * datapath key types are added. */
-BUILD_ASSERT_DECL(__OVS_KEY_ATTR_MAX == 14);
+BUILD_ASSERT_DECL(__OVS_KEY_ATTR_MAX == 15);
 
 /* A buffer with sufficient size and alignment to hold an nlattr-formatted flow
  * key.  An array of "struct nlattr" might not, in theory, be sufficiently
diff --git a/ofproto/in-band.c b/ofproto/in-band.c
index cd9c050..8ec1357 100644
--- a/ofproto/in-band.c
+++ b/ofproto/in-band.c
@@ -266,7 +266,8 @@ in_band_rule_check(const struct flow *flow,
         unsigned int left;
 
         NL_ATTR_FOR_EACH_UNSAFE (a, left, actions, actions_len) {
-            if (nl_attr_type(a) == OVS_ACTION_ATTR_OUTPUT
+            if (nl_attr_type(a) == OVS_ACT_TYPE(OVS_ACTION_ATTR_OUTPUT,
+                                                OVS_KEY_ATTR_NONE)
                 && nl_attr_get_u32(a) == OVSP_LOCAL) {
                 return true;
             }
diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c
index 9e8b1fb..62f4bc6 100644
--- a/ofproto/ofproto-dpif.c
+++ b/ofproto/ofproto-dpif.c
@@ -2207,7 +2207,8 @@ execute_odp_actions(struct ofproto_dpif *ofproto, const 
struct flow *flow,
     int error;
 
     if (actions_len == NLA_ALIGN(NLA_HDRLEN + sizeof(uint64_t))
-        && odp_actions->nla_type == OVS_ACTION_ATTR_USERSPACE) {
+        && odp_actions->nla_type == OVS_ACT_TYPE(OVS_ACTION_ATTR_USERSPACE,
+                                                 OVS_KEY_ATTR_NONE)) {
         const struct user_action_cookie *cookie;
         struct dpif_upcall upcall;
 
@@ -2400,9 +2401,10 @@ facet_account(struct ofproto_dpif *ofproto, struct facet 
*facet)
     vlan_tci = facet->flow.vlan_tci;
     NL_ATTR_FOR_EACH_UNSAFE (a, left, facet->actions, facet->actions_len) {
         struct ofport_dpif *port;
+        const struct ovs_key_8021q *q_key;
 
         switch (nl_attr_type(a)) {
-        case OVS_ACTION_ATTR_OUTPUT:
+        case OVS_ACT_TYPE(OVS_ACTION_ATTR_OUTPUT, OVS_KEY_ATTR_NONE):
             port = get_odp_port(ofproto, nl_attr_get_u32(a));
             if (port && port->bundle && port->bundle->bond) {
                 bond_account(port->bundle->bond, &facet->flow,
@@ -2410,12 +2412,13 @@ facet_account(struct ofproto_dpif *ofproto, struct 
facet *facet)
             }
             break;
 
-        case OVS_ACTION_ATTR_POP_VLAN:
+        case OVS_ACT_TYPE(OVS_ACTION_ATTR_POP, OVS_KEY_ATTR_8021Q):
             vlan_tci = htons(0);
             break;
 
-        case OVS_ACTION_ATTR_PUSH_VLAN:
-            vlan_tci = nl_attr_get_be16(a);
+        case OVS_ACT_TYPE(OVS_ACTION_ATTR_PUSH, OVS_KEY_ATTR_8021Q):
+            q_key = nl_attr_get_unspec(a, sizeof(*q_key));
+            vlan_tci = q_key->q_tci;
             break;
         }
     }
@@ -2936,6 +2939,7 @@ send_packet(struct ofproto_dpif *ofproto, uint32_t 
odp_port,
     struct odputil_keybuf keybuf;
     struct flow flow;
     int error;
+    uint16_t type;
 
     flow_extract((struct ofpbuf *) packet, 0, 0, &flow);
     ofpbuf_use_stack(&key, &keybuf, sizeof keybuf);
@@ -2944,7 +2948,8 @@ send_packet(struct ofproto_dpif *ofproto, uint32_t 
odp_port,
     ofpbuf_init(&odp_actions, 32);
     compose_sflow_action(ofproto, &odp_actions, &flow, odp_port);
 
-    nl_msg_put_u32(&odp_actions, OVS_ACTION_ATTR_OUTPUT, odp_port);
+    type = OVS_ACT_TYPE(OVS_ACTION_ATTR_OUTPUT, OVS_KEY_ATTR_NONE);
+    nl_msg_put_u32(&odp_actions, type, odp_port);
     error = dpif_execute(ofproto->dpif,
                          key.data, key.size,
                          odp_actions.data, odp_actions.size,
@@ -2976,6 +2981,7 @@ compose_sflow_action(const struct ofproto_dpif *ofproto,
     struct user_action_cookie *cookie;
     size_t sample_offset, actions_offset;
     int user_cookie_offset, n_output;
+    uint16_t user_action, sample_action;
 
     if (!ofproto->sflow || flow->in_port == OFPP_NONE) {
         return 0;
@@ -2989,7 +2995,8 @@ compose_sflow_action(const struct ofproto_dpif *ofproto,
         n_output = 1;
     }
 
-    sample_offset = nl_msg_start_nested(odp_actions, OVS_ACTION_ATTR_SAMPLE);
+    sample_action = OVS_ACT_TYPE(OVS_ACTION_ATTR_SAMPLE, OVS_KEY_ATTR_NONE);
+    sample_offset = nl_msg_start_nested(odp_actions, sample_action);
 
     /* Number of packets out of UINT_MAX to sample. */
     probability = dpif_sflow_get_probability(ofproto->sflow);
@@ -2997,7 +3004,8 @@ compose_sflow_action(const struct ofproto_dpif *ofproto,
 
     actions_offset = nl_msg_start_nested(odp_actions, OVS_SAMPLE_ATTR_ACTIONS);
 
-    cookie = nl_msg_put_unspec_uninit(odp_actions, OVS_ACTION_ATTR_USERSPACE,
+    user_action = OVS_ACT_TYPE(OVS_ACTION_ATTR_USERSPACE, OVS_KEY_ATTR_NONE);
+    cookie = nl_msg_put_unspec_uninit(odp_actions, user_action,
                                                 sizeof(*cookie));
     cookie->type = USER_ACTION_COOKIE_SFLOW;
     cookie->data = port_ifindex;
@@ -3054,91 +3062,173 @@ fix_sflow_action(struct action_xlate_ctx *ctx)
 }
 
 static void
-commit_vlan_tci(struct action_xlate_ctx *ctx, ovs_be16 vlan_tci)
+commit_vlan_action(struct action_xlate_ctx *ctx, ovs_be16 new_tci)
 {
     struct flow *base = &ctx->base_flow;
-    struct ofpbuf *odp_actions = ctx->odp_actions;
+    uint16_t type;
 
-    if (base->vlan_tci != vlan_tci) {
-        if (!(vlan_tci & htons(VLAN_CFI))) {
-            nl_msg_put_flag(odp_actions, OVS_ACTION_ATTR_POP_VLAN);
-        } else {
-            if (base->vlan_tci != htons(0)) {
-                nl_msg_put_flag(odp_actions, OVS_ACTION_ATTR_POP_VLAN);
-            }
-            nl_msg_put_be16(odp_actions, OVS_ACTION_ATTR_PUSH_VLAN,
-                            vlan_tci & ~htons(VLAN_CFI));
+    if (base->vlan_tci == new_tci) {
+        return;
+    }
+
+    if (!(new_tci & htons(VLAN_CFI))) {
+        type = OVS_ACT_TYPE(OVS_ACTION_ATTR_POP, OVS_KEY_ATTR_8021Q);
+        nl_msg_put_flag(ctx->odp_actions, type);
+    } else {
+        struct ovs_key_8021q q_key;
+
+        if (base->vlan_tci != htons(0)) {
+            type = OVS_ACT_TYPE(OVS_ACTION_ATTR_POP, OVS_KEY_ATTR_8021Q);
+            nl_msg_put_flag(ctx->odp_actions, type);
         }
-        base->vlan_tci = vlan_tci;
+        type = OVS_ACT_TYPE(OVS_ACTION_ATTR_PUSH, OVS_KEY_ATTR_8021Q);
+        q_key.q_tpid = 0;
+        q_key.q_tci = new_tci & ~htons(VLAN_CFI);
+        nl_msg_put_unspec(ctx->odp_actions, type, &q_key, sizeof(q_key));
     }
+    base->vlan_tci = new_tci;
 }
 
 static void
-commit_odp_actions(struct action_xlate_ctx *ctx)
+commit_set_tun_id_action(const struct flow *flow, struct flow *base,
+                         struct ofpbuf *odp_actions)
 {
-    const struct flow *flow = &ctx->flow;
-    struct flow *base = &ctx->base_flow;
-    struct ofpbuf *odp_actions = ctx->odp_actions;
+    uint16_t type;
 
-    if (base->tun_id != flow->tun_id) {
-        nl_msg_put_be64(odp_actions, OVS_ACTION_ATTR_SET_TUNNEL, flow->tun_id);
-        base->tun_id = flow->tun_id;
+    if (base->tun_id == flow->tun_id) {
+        return;
     }
+    base->tun_id = flow->tun_id;
+    type = OVS_ACT_TYPE(OVS_ACTION_ATTR_SET, OVS_KEY_ATTR_TUN_ID);
+    nl_msg_put_be64(odp_actions, type, base->tun_id);
+}
 
-    if (base->nw_src != flow->nw_src) {
-        nl_msg_put_be32(odp_actions, OVS_ACTION_ATTR_SET_NW_SRC, flow->nw_src);
-        base->nw_src = flow->nw_src;
-    }
+static void
+commit_set_port_action(const struct flow *flow, struct flow *base,
+                       struct ofpbuf *odp_actions)
+{
+    uint16_t type;
 
-    if (base->nw_dst != flow->nw_dst) {
-        nl_msg_put_be32(odp_actions, OVS_ACTION_ATTR_SET_NW_DST, flow->nw_dst);
-        base->nw_dst = flow->nw_dst;
+    if (base->tp_src == flow->tp_src &&
+        base->tp_dst == flow->tp_dst) {
+        return;
     }
 
-    if (base->nw_tos != flow->nw_tos) {
-        nl_msg_put_u8(odp_actions, OVS_ACTION_ATTR_SET_NW_TOS, flow->nw_tos);
-        base->nw_tos = flow->nw_tos;
-    }
+    base->tp_src = flow->tp_src;
+    base->tp_dst = flow->tp_dst;
 
-    commit_vlan_tci(ctx, flow->vlan_tci);
+    if (flow->nw_proto == IPPROTO_TCP) {
+        struct ovs_key_tcp port_key;
 
-    if (base->tp_src != flow->tp_src) {
-        nl_msg_put_be16(odp_actions, OVS_ACTION_ATTR_SET_TP_SRC, flow->tp_src);
-        base->tp_src = flow->tp_src;
-    }
+        port_key.tcp_src = base->tp_src;
+        port_key.tcp_dst = base->tp_dst;
+
+        type = OVS_ACT_TYPE(OVS_ACTION_ATTR_SET, OVS_KEY_ATTR_TCP);
+        nl_msg_put_unspec(odp_actions, type, &port_key, sizeof(port_key));
+    } else if (flow->nw_proto == IPPROTO_UDP) {
+        struct ovs_key_udp port_key;
+
+        port_key.udp_src = base->tp_src ;
+        port_key.udp_dst = base->tp_dst;
 
-    if (base->tp_dst != flow->tp_dst) {
-        nl_msg_put_be16(odp_actions, OVS_ACTION_ATTR_SET_TP_DST, flow->tp_dst);
-        base->tp_dst = flow->tp_dst;
+        type = OVS_ACT_TYPE(OVS_ACTION_ATTR_SET, OVS_KEY_ATTR_UDP);
+        nl_msg_put_unspec(odp_actions, type, &port_key, sizeof(port_key));
+   } else {
+        VLOG_WARN_RL(&rl, "Unknow protocol : %"PRIu16, flow->nw_proto);
+   }
+
+}
+
+static void
+commit_set_nw_action(const struct flow *flow, struct flow *base,
+                     struct ofpbuf *odp_actions)
+{
+    struct ovs_key_ipv4 ipv4_key;
+    uint16_t type;
+
+    if (base->nw_src == flow->nw_src &&
+        base->nw_dst == flow->nw_dst &&
+        base->nw_tos == flow->nw_tos) {
+        return ;
     }
 
-    if (!eth_addr_equals(base->dl_src, flow->dl_src)) {
-        nl_msg_put_unspec(odp_actions, OVS_ACTION_ATTR_SET_DL_SRC,
-                          flow->dl_src, ETH_ADDR_LEN);
-        memcpy(base->dl_src, flow->dl_src, ETH_ADDR_LEN);
+    base->nw_src = flow->nw_src;
+    base->nw_dst = flow->nw_dst;
+    base->nw_tos = flow->nw_tos;
+
+    ipv4_key.ipv4_src = base->nw_src;
+    ipv4_key.ipv4_dst = base->nw_dst;
+    ipv4_key.ipv4_tos = base->nw_tos;
+
+    type = OVS_ACT_TYPE(OVS_ACTION_ATTR_SET, OVS_KEY_ATTR_IPV4);
+    nl_msg_put_unspec(odp_actions, type, &ipv4_key, sizeof(ipv4_key));
+}
+
+static void
+commit_set_ether_addr_action(const struct flow *flow, struct flow *base,
+                             struct ofpbuf *odp_actions)
+{
+    struct ovs_key_ethernet eth_key;
+    uint16_t type;
+
+    if (eth_addr_equals(base->dl_src, flow->dl_src) &&
+        eth_addr_equals(base->dl_dst, flow->dl_dst)) {
+        return;
     }
+    memcpy(base->dl_src, flow->dl_src, ETH_ADDR_LEN);
+    memcpy(base->dl_dst, flow->dl_dst, ETH_ADDR_LEN);
+
+    memcpy(eth_key.eth_src, base->dl_src, ETH_ADDR_LEN);
+    memcpy(eth_key.eth_dst, base->dl_dst, ETH_ADDR_LEN);
+
+    type = OVS_ACT_TYPE(OVS_ACTION_ATTR_SET, OVS_KEY_ATTR_ETHERNET);
+    nl_msg_put_unspec(odp_actions, type, &eth_key, sizeof(eth_key));
+}
+
+static void
+commit_priority_action(struct action_xlate_ctx *ctx)
+{
+    struct ofpbuf *odp_actions;
+    uint16_t type;
 
-    if (!eth_addr_equals(base->dl_dst, flow->dl_dst)) {
-        nl_msg_put_unspec(odp_actions, OVS_ACTION_ATTR_SET_DL_DST,
-                          flow->dl_dst, ETH_ADDR_LEN);
-        memcpy(base->dl_dst, flow->dl_dst, ETH_ADDR_LEN);
+    if (ctx->base_priority == ctx->priority) {
+        return;
     }
 
-    if (ctx->base_priority != ctx->priority) {
-        if (ctx->priority) {
-            nl_msg_put_u32(odp_actions, OVS_ACTION_ATTR_SET_PRIORITY,
-                           ctx->priority);
-        } else {
-            nl_msg_put_flag(odp_actions, OVS_ACTION_ATTR_POP_PRIORITY);
-        }
-        ctx->base_priority = ctx->priority;
+    odp_actions = ctx->odp_actions;
+    if (ctx->priority) {
+        type = OVS_ACT_TYPE(OVS_ACTION_ATTR_SET_PRIORITY, OVS_KEY_ATTR_NONE);
+        nl_msg_put_u32(odp_actions, type, ctx->priority);
+    } else {
+        type = OVS_ACT_TYPE(OVS_ACTION_ATTR_RESTORE_PRIORITY,
+                                OVS_KEY_ATTR_NONE);
+        nl_msg_put_flag(odp_actions, type);
     }
+    ctx->base_priority = ctx->priority;
+}
+
+static void
+commit_odp_actions(struct action_xlate_ctx *ctx)
+{
+    const struct flow *flow = &ctx->flow;
+    struct flow *base = &ctx->base_flow;
+    struct ofpbuf *odp_actions = ctx->odp_actions;
+
+    commit_set_tun_id_action(flow, base, odp_actions);
+    commit_set_nw_action(flow, base, odp_actions);
+    commit_set_port_action(flow, base, odp_actions);
+    commit_set_ether_addr_action(flow, base, odp_actions);
+    commit_priority_action(ctx);
+    commit_vlan_action(ctx, flow->vlan_tci);
 }
 
 static void
 compose_output_action(struct action_xlate_ctx *ctx, uint16_t odp_port)
 {
-    nl_msg_put_u32(ctx->odp_actions, OVS_ACTION_ATTR_OUTPUT, odp_port);
+    uint16_t type;
+
+    type = OVS_ACT_TYPE(OVS_ACTION_ATTR_OUTPUT, OVS_KEY_ATTR_NONE);
+    nl_msg_put_u32(ctx->odp_actions, type, odp_port);
     ctx->sflow_odp_port = odp_port;
     ctx->sflow_n_outputs++;
 }
@@ -3255,14 +3345,15 @@ static void
 compose_controller_action(struct ofpbuf *odp_actions, int len)
 {
     struct user_action_cookie cookie;
+    uint16_t type;
 
     cookie.type = USER_ACTION_COOKIE_CONTROLLER;
     cookie.data = len;
     cookie.n_output = 0;
     cookie.vlan_tci = 0;
 
-    nl_msg_put_unspec(odp_actions, OVS_ACTION_ATTR_USERSPACE,
-                                       &cookie, sizeof(cookie));
+    type = OVS_ACT_TYPE(OVS_ACTION_ATTR_USERSPACE, OVS_KEY_ATTR_NONE);
+    nl_msg_put_unspec(odp_actions, type, &cookie, sizeof(cookie));
 }
 
 static void
@@ -4010,7 +4101,7 @@ compose_actions(struct action_xlate_ctx *ctx, uint16_t 
vlan,
             if (tci) {
                 tci |= htons(VLAN_CFI);
             }
-            commit_vlan_tci(ctx, tci);
+            commit_vlan_action(ctx, tci);
 
             cur_vid = dst->vid;
         }
@@ -4643,7 +4734,8 @@ ofproto_unixctl_trace(struct unixctl_conn *conn, const 
char *args_,
         ds_put_char(&result, '\n');
         trace_format_flow(&result, 0, "Final flow", &trace);
         ds_put_cstr(&result, "Datapath actions: ");
-        format_odp_actions(&result, odp_actions->data, odp_actions->size);
+        format_odp_actions(&result, odp_actions->data, odp_actions->size,
+                           &trace.ctx.base_flow);
         ofpbuf_delete(odp_actions);
 
         if (!trace.ctx.may_set_up_flow) {
diff --git a/utilities/ovs-dpctl.c b/utilities/ovs-dpctl.c
index 4d0d3c2..aab0695 100644
--- a/utilities/ovs-dpctl.c
+++ b/utilities/ovs-dpctl.c
@@ -42,6 +42,7 @@
 #include "timeval.h"
 #include "util.h"
 #include "vlog.h"
+#include "lib/flow.h"
 
 VLOG_DEFINE_THIS_MODULE(dpctl);
 
@@ -569,12 +570,19 @@ do_dump_flows(int argc OVS_UNUSED, char *argv[])
     dpif_flow_dump_start(&dump, dpif);
     while (dpif_flow_dump_next(&dump, &key, &key_len,
                                &actions, &actions_len, &stats)) {
+        struct flow flow;
+
         ds_clear(&ds);
         odp_flow_key_format(key, key_len, &ds);
         ds_put_cstr(&ds, ", ");
         dpif_flow_stats_format(stats, &ds);
         ds_put_cstr(&ds, ", actions:");
-        format_odp_actions(&ds, actions, actions_len);
+        if (odp_flow_key_to_flow(key, key_len, &flow)) {
+            ds_put_cstr(&ds, "invalid key");
+            printf("%s\n", ds_cstr(&ds));
+            continue;
+        }
+        format_odp_actions(&ds, actions, actions_len, &flow);
         printf("%s\n", ds_cstr(&ds));
     }
     dpif_flow_dump_done(&dump);
-- 
1.7.1

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

Reply via email to