flow: New FLOW_WC_SEQ build assertion.
[sliver-openvswitch.git] / lib / ofp-util.c
index 2d10fcb..b0aeaf4 100644 (file)
@@ -21,6 +21,7 @@
 #include <netinet/icmp6.h>
 #include <stdlib.h>
 #include "autopath.h"
+#include "bundle.h"
 #include "byte-order.h"
 #include "classifier.h"
 #include "dynamic-string.h"
@@ -107,6 +108,8 @@ static const flow_wildcards_t WC_INVARIANTS = 0
 void
 ofputil_wildcard_from_openflow(uint32_t ofpfw, struct flow_wildcards *wc)
 {
+    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 1);
+
     /* Initialize most of rule->wc. */
     flow_wildcards_init_catchall(wc);
     wc->wildcards = (OVS_FORCE flow_wildcards_t) ofpfw & WC_INVARIANTS;
@@ -799,6 +802,8 @@ ofputil_min_flow_format(const struct cls_rule *rule)
 {
     const struct flow_wildcards *wc = &rule->wc;
 
+    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 1);
+
     /* Only NXM supports separately wildcards the Ethernet multicast bit. */
     if (!(wc->wildcards & FWW_DL_DST) != !(wc->wildcards & FWW_ETH_MCAST)) {
         return NXFF_NXM;
@@ -865,8 +870,8 @@ ofputil_make_flow_mod_table_id(bool flow_mod_table_id)
  *
  * Does not validate the flow_mod actions. */
 int
-ofputil_decode_flow_mod(struct flow_mod *fm, const struct ofp_header *oh,
-                        bool flow_mod_table_id)
+ofputil_decode_flow_mod(struct ofputil_flow_mod *fm,
+                        const struct ofp_header *oh, bool flow_mod_table_id)
 {
     const struct ofputil_msg_type *type;
     uint16_t command;
@@ -955,7 +960,7 @@ ofputil_decode_flow_mod(struct flow_mod *fm, const struct ofp_header *oh,
  * 'flow_mod_table_id' should be true if the NXT_FLOW_MOD_TABLE_ID extension is
  * enabled, false otherwise. */
 struct ofpbuf *
-ofputil_encode_flow_mod(const struct flow_mod *fm,
+ofputil_encode_flow_mod(const struct ofputil_flow_mod *fm,
                         enum nx_flow_format flow_format,
                         bool flow_mod_table_id)
 {
@@ -1009,7 +1014,7 @@ ofputil_encode_flow_mod(const struct flow_mod *fm,
 }
 
 static int
-ofputil_decode_ofpst_flow_request(struct flow_stats_request *fsr,
+ofputil_decode_ofpst_flow_request(struct ofputil_flow_stats_request *fsr,
                                   const struct ofp_header *oh,
                                   bool aggregate)
 {
@@ -1025,7 +1030,7 @@ ofputil_decode_ofpst_flow_request(struct flow_stats_request *fsr,
 }
 
 static int
-ofputil_decode_nxst_flow_request(struct flow_stats_request *fsr,
+ofputil_decode_nxst_flow_request(struct ofputil_flow_stats_request *fsr,
                                  const struct ofp_header *oh,
                                  bool aggregate)
 {
@@ -1055,7 +1060,7 @@ ofputil_decode_nxst_flow_request(struct flow_stats_request *fsr,
  * request 'oh', into an abstract flow_stats_request in 'fsr'.  Returns 0 if
  * successful, otherwise an OpenFlow error code. */
 int
-ofputil_decode_flow_stats_request(struct flow_stats_request *fsr,
+ofputil_decode_flow_stats_request(struct ofputil_flow_stats_request *fsr,
                                   const struct ofp_header *oh)
 {
     const struct ofputil_msg_type *type;
@@ -1089,7 +1094,7 @@ ofputil_decode_flow_stats_request(struct flow_stats_request *fsr,
  * OFPST_AGGREGATE, NXST_FLOW, or NXST_AGGREGATE request 'oh' according to
  * 'flow_format', and returns the message. */
 struct ofpbuf *
-ofputil_encode_flow_stats_request(const struct flow_stats_request *fsr,
+ofputil_encode_flow_stats_request(const struct ofputil_flow_stats_request *fsr,
                                   enum nx_flow_format flow_format)
 {
     struct ofpbuf *msg;
@@ -1941,38 +1946,11 @@ make_echo_reply(const struct ofp_header *rq)
     return out;
 }
 
-static int
-check_action_exact_len(const union ofp_action *a, unsigned int len,
-                       unsigned int required_len)
-{
-    if (len != required_len) {
-        VLOG_WARN_RL(&bad_ofmsg_rl, "action %"PRIu16" has invalid length "
-                     "%"PRIu16" (must be %u)\n",
-                     ntohs(a->type), ntohs(a->header.len), required_len);
-        return ofp_mkerr(OFPET_BAD_ACTION, OFPBAC_BAD_LEN);
-    }
-    return 0;
-}
-
-static int
-check_nx_action_exact_len(const struct nx_action_header *a,
-                          unsigned int len, unsigned int required_len)
-{
-    if (len != required_len) {
-        VLOG_WARN_RL(&bad_ofmsg_rl,
-                     "Nicira action %"PRIu16" has invalid length %"PRIu16" "
-                     "(must be %u)\n",
-                     ntohs(a->subtype), ntohs(a->len), required_len);
-        return ofp_mkerr(OFPET_BAD_ACTION, OFPBAC_BAD_LEN);
-    }
-    return 0;
-}
-
 /* Checks that 'port' is a valid output port for the OFPAT_OUTPUT action, given
  * that the switch will never have more than 'max_ports' ports.  Returns 0 if
  * 'port' is valid, otherwise an ofp_mkerr() return code. */
-static int
-check_output_port(uint16_t port, int max_ports)
+int
+ofputil_check_output_port(uint16_t port, int max_ports)
 {
     switch (port) {
     case OFPP_IN_PORT:
@@ -1988,208 +1966,272 @@ check_output_port(uint16_t port, int max_ports)
         if (port < max_ports) {
             return 0;
         }
-        VLOG_WARN_RL(&bad_ofmsg_rl, "unknown output port %x", port);
         return ofp_mkerr(OFPET_BAD_ACTION, OFPBAC_BAD_OUT_PORT);
     }
 }
 
-/* Checks that 'action' is a valid OFPAT_ENQUEUE action, given that the switch
- * will never have more than 'max_ports' ports.  Returns 0 if 'port' is valid,
- * otherwise an ofp_mkerr() return code. */
 static int
-check_enqueue_action(const union ofp_action *a, unsigned int len,
-                     int max_ports)
+check_resubmit_table(const struct nx_action_resubmit *nar)
 {
-    const struct ofp_action_enqueue *oae;
-    uint16_t port;
-    int error;
-
-    error = check_action_exact_len(a, len, 16);
-    if (error) {
-        return error;
+    if (nar->pad[0] || nar->pad[1] || nar->pad[2]) {
+        return ofp_mkerr(OFPET_BAD_ACTION, OFPBAC_BAD_ARGUMENT);
     }
-
-    oae = (const struct ofp_action_enqueue *) a;
-    port = ntohs(oae->port);
-    if (port < max_ports || port == OFPP_IN_PORT) {
-        return 0;
-    }
-    VLOG_WARN_RL(&bad_ofmsg_rl, "unknown enqueue port %x", port);
-    return ofp_mkerr(OFPET_BAD_ACTION, OFPBAC_BAD_OUT_PORT);
+    return 0;
 }
 
-static int
-check_nicira_action(const union ofp_action *a, unsigned int len,
-                    const struct flow *flow)
+int
+validate_actions(const union ofp_action *actions, size_t n_actions,
+                 const struct flow *flow, int max_ports)
 {
-    const struct nx_action_header *nah;
-    int subtype;
-    int error;
+    const union ofp_action *a;
+    size_t left;
 
-    if (len < 16) {
-        VLOG_WARN_RL(&bad_ofmsg_rl,
-                     "Nicira vendor action only %u bytes", len);
-        return ofp_mkerr(OFPET_BAD_ACTION, OFPBAC_BAD_LEN);
-    }
-    nah = (const struct nx_action_header *) a;
+    OFPUTIL_ACTION_FOR_EACH (a, left, actions, n_actions) {
+        uint16_t port;
+        int error;
+        int code;
 
-    subtype = ntohs(nah->subtype);
-    if (subtype > TYPE_MAXIMUM(enum nx_action_subtype)) {
-        /* This is necessary because enum nx_action_subtype may be an
-         * 8-bit type, so the cast below throws away the top 8 bits. */
-        return ofp_mkerr(OFPET_BAD_ACTION, OFPBAC_BAD_VENDOR_TYPE);
-    }
+        code = ofputil_decode_action(a);
+        if (code < 0) {
+            char *msg;
 
-    switch ((enum nx_action_subtype) subtype) {
-    case NXAST_RESUBMIT:
-    case NXAST_SET_TUNNEL:
-    case NXAST_SET_QUEUE:
-    case NXAST_POP_QUEUE:
-        return check_nx_action_exact_len(nah, len, 16);
+            error = -code;
+            msg = ofputil_error_to_string(error);
+            VLOG_WARN_RL(&bad_ofmsg_rl,
+                         "action decoding error at offset %td (%s)",
+                         (a - actions) * sizeof *a, msg);
+            free(msg);
 
-    case NXAST_REG_MOVE:
-        error = check_nx_action_exact_len(nah, len,
-                                          sizeof(struct nx_action_reg_move));
-        if (error) {
             return error;
         }
-        return nxm_check_reg_move((const struct nx_action_reg_move *) a, flow);
 
-    case NXAST_REG_LOAD:
-        error = check_nx_action_exact_len(nah, len,
-                                          sizeof(struct nx_action_reg_load));
-        if (error) {
-            return error;
-        }
-        return nxm_check_reg_load((const struct nx_action_reg_load *) a, flow);
+        error = 0;
+        switch ((enum ofputil_action_code) code) {
+        case OFPUTIL_OFPAT_OUTPUT:
+            error = ofputil_check_output_port(ntohs(a->output.port),
+                                              max_ports);
+            break;
 
-    case NXAST_NOTE:
-        return 0;
+        case OFPUTIL_OFPAT_SET_VLAN_VID:
+            if (a->vlan_vid.vlan_vid & ~htons(0xfff)) {
+                error = ofp_mkerr(OFPET_BAD_ACTION, OFPBAC_BAD_ARGUMENT);
+            }
+            break;
 
-    case NXAST_SET_TUNNEL64:
-        return check_nx_action_exact_len(
-            nah, len, sizeof(struct nx_action_set_tunnel64));
+        case OFPUTIL_OFPAT_SET_VLAN_PCP:
+            if (a->vlan_pcp.vlan_pcp & ~7) {
+                error = ofp_mkerr(OFPET_BAD_ACTION, OFPBAC_BAD_ARGUMENT);
+            }
+            break;
 
-    case NXAST_MULTIPATH:
-        error = check_nx_action_exact_len(
-            nah, len, sizeof(struct nx_action_multipath));
-        if (error) {
-            return error;
+        case OFPUTIL_OFPAT_ENQUEUE:
+            port = ntohs(((const struct ofp_action_enqueue *) a)->port);
+            if (port >= max_ports && port != OFPP_IN_PORT) {
+                error = ofp_mkerr(OFPET_BAD_ACTION, OFPBAC_BAD_OUT_PORT);
+            }
+            break;
+
+        case OFPUTIL_NXAST_REG_MOVE:
+            error = nxm_check_reg_move((const struct nx_action_reg_move *) a,
+                                       flow);
+            break;
+
+        case OFPUTIL_NXAST_REG_LOAD:
+            error = nxm_check_reg_load((const struct nx_action_reg_load *) a,
+                                       flow);
+            break;
+
+        case OFPUTIL_NXAST_MULTIPATH:
+            error = multipath_check((const struct nx_action_multipath *) a,
+                                    flow);
+            break;
+
+        case OFPUTIL_NXAST_AUTOPATH:
+            error = autopath_check((const struct nx_action_autopath *) a,
+                                   flow);
+            break;
+
+        case OFPUTIL_NXAST_BUNDLE:
+        case OFPUTIL_NXAST_BUNDLE_LOAD:
+            error = bundle_check((const struct nx_action_bundle *) a,
+                                 max_ports, flow);
+            break;
+
+        case OFPUTIL_NXAST_RESUBMIT_TABLE:
+            error = check_resubmit_table(
+                (const struct nx_action_resubmit *) a);
+            break;
+
+        case OFPUTIL_OFPAT_STRIP_VLAN:
+        case OFPUTIL_OFPAT_SET_NW_SRC:
+        case OFPUTIL_OFPAT_SET_NW_DST:
+        case OFPUTIL_OFPAT_SET_NW_TOS:
+        case OFPUTIL_OFPAT_SET_TP_SRC:
+        case OFPUTIL_OFPAT_SET_TP_DST:
+        case OFPUTIL_OFPAT_SET_DL_SRC:
+        case OFPUTIL_OFPAT_SET_DL_DST:
+        case OFPUTIL_NXAST_RESUBMIT:
+        case OFPUTIL_NXAST_SET_TUNNEL:
+        case OFPUTIL_NXAST_SET_QUEUE:
+        case OFPUTIL_NXAST_POP_QUEUE:
+        case OFPUTIL_NXAST_NOTE:
+        case OFPUTIL_NXAST_SET_TUNNEL64:
+            break;
         }
-        return multipath_check((const struct nx_action_multipath *) a);
 
-    case NXAST_AUTOPATH:
-        error = check_nx_action_exact_len(
-            nah, len, sizeof(struct nx_action_autopath));
         if (error) {
+            char *msg = ofputil_error_to_string(error);
+            VLOG_WARN_RL(&bad_ofmsg_rl, "bad action at offset %td (%s)",
+                         (a - actions) * sizeof *a, msg);
+            free(msg);
             return error;
         }
-        return autopath_check((const struct nx_action_autopath *) a);
-
-    case NXAST_SNAT__OBSOLETE:
-    case NXAST_DROP_SPOOFED_ARP__OBSOLETE:
-    default:
-        VLOG_WARN_RL(&bad_ofmsg_rl,
-                     "unknown Nicira vendor action subtype %d", subtype);
-        return ofp_mkerr(OFPET_BAD_ACTION, OFPBAC_BAD_VENDOR_TYPE);
     }
+    if (left) {
+        VLOG_WARN_RL(&bad_ofmsg_rl, "bad action format at offset %zu",
+                     (n_actions - left) * sizeof *a);
+        return ofp_mkerr(OFPET_BAD_ACTION, OFPBAC_BAD_LEN);
+    }
+    return 0;
 }
 
-static int
-check_action(const union ofp_action *a, unsigned int len,
-             const struct flow *flow, int max_ports)
+struct ofputil_action {
+    int code;
+    unsigned int min_len;
+    unsigned int max_len;
+};
+
+static const struct ofputil_action action_bad_type
+    = { -OFP_MKERR(OFPET_BAD_ACTION, OFPBAC_BAD_TYPE),   0, UINT_MAX };
+static const struct ofputil_action action_bad_len
+    = { -OFP_MKERR(OFPET_BAD_ACTION, OFPBAC_BAD_LEN),    0, UINT_MAX };
+static const struct ofputil_action action_bad_vendor
+    = { -OFP_MKERR(OFPET_BAD_ACTION, OFPBAC_BAD_VENDOR), 0, UINT_MAX };
+
+static const struct ofputil_action *
+ofputil_decode_ofpat_action(const union ofp_action *a)
 {
     enum ofp_action_type type = ntohs(a->type);
-    int error;
 
     switch (type) {
-    case OFPAT_OUTPUT:
-        error = check_action_exact_len(a, len, 8);
-        if (error) {
-            return error;
-        }
-        return check_output_port(ntohs(a->output.port), max_ports);
-
-    case OFPAT_SET_VLAN_VID:
-        error = check_action_exact_len(a, len, 8);
-        if (error) {
-            return error;
-        }
-        if (a->vlan_vid.vlan_vid & ~htons(0xfff)) {
-            return ofp_mkerr(OFPET_BAD_ACTION, OFPBAC_BAD_ARGUMENT);
+#define OFPAT_ACTION(ENUM, TYPE)                            \
+        case ENUM: {                                        \
+            static const struct ofputil_action action = {   \
+                OFPUTIL_##ENUM, sizeof(TYPE), sizeof(TYPE)  \
+            };                                              \
+            return &action;                                 \
         }
-        return 0;
-
-    case OFPAT_SET_VLAN_PCP:
-        error = check_action_exact_len(a, len, 8);
-        if (error) {
-            return error;
-        }
-        if (a->vlan_pcp.vlan_pcp & ~7) {
-            return ofp_mkerr(OFPET_BAD_ACTION, OFPBAC_BAD_ARGUMENT);
-        }
-        return 0;
-
-    case OFPAT_STRIP_VLAN:
-    case OFPAT_SET_NW_SRC:
-    case OFPAT_SET_NW_DST:
-    case OFPAT_SET_NW_TOS:
-    case OFPAT_SET_TP_SRC:
-    case OFPAT_SET_TP_DST:
-        return check_action_exact_len(a, len, 8);
-
-    case OFPAT_SET_DL_SRC:
-    case OFPAT_SET_DL_DST:
-        return check_action_exact_len(a, len, 16);
+        OFPAT_ACTION(OFPAT_OUTPUT,       struct ofp_action_output);
+        OFPAT_ACTION(OFPAT_SET_VLAN_VID, struct ofp_action_vlan_vid);
+        OFPAT_ACTION(OFPAT_SET_VLAN_PCP, struct ofp_action_vlan_pcp);
+        OFPAT_ACTION(OFPAT_STRIP_VLAN,   struct ofp_action_header);
+        OFPAT_ACTION(OFPAT_SET_DL_SRC,   struct ofp_action_dl_addr);
+        OFPAT_ACTION(OFPAT_SET_DL_DST,   struct ofp_action_dl_addr);
+        OFPAT_ACTION(OFPAT_SET_NW_SRC,   struct ofp_action_nw_addr);
+        OFPAT_ACTION(OFPAT_SET_NW_DST,   struct ofp_action_nw_addr);
+        OFPAT_ACTION(OFPAT_SET_NW_TOS,   struct ofp_action_nw_tos);
+        OFPAT_ACTION(OFPAT_SET_TP_SRC,   struct ofp_action_tp_port);
+        OFPAT_ACTION(OFPAT_SET_TP_DST,   struct ofp_action_tp_port);
+        OFPAT_ACTION(OFPAT_ENQUEUE,      struct ofp_action_enqueue);
+#undef OFPAT_ACTION
 
     case OFPAT_VENDOR:
-        return (a->vendor.vendor == htonl(NX_VENDOR_ID)
-                ? check_nicira_action(a, len, flow)
-                : ofp_mkerr(OFPET_BAD_ACTION, OFPBAC_BAD_VENDOR));
+    default:
+        return &action_bad_type;
+    }
+}
 
-    case OFPAT_ENQUEUE:
-        return check_enqueue_action(a, len, max_ports);
+static const struct ofputil_action *
+ofputil_decode_nxast_action(const union ofp_action *a)
+{
+    const struct nx_action_header *nah = (const struct nx_action_header *) a;
+    enum nx_action_subtype subtype = ntohs(nah->subtype);
+
+    switch (subtype) {
+#define NXAST_ACTION(ENUM, TYPE, EXTENSIBLE)                \
+        case ENUM: {                                        \
+            static const struct ofputil_action action = {   \
+                OFPUTIL_##ENUM,                             \
+                sizeof(TYPE),                               \
+                EXTENSIBLE ? UINT_MAX : sizeof(TYPE)        \
+            };                                              \
+            return &action;                                 \
+        }
+        NXAST_ACTION(NXAST_RESUBMIT,     struct nx_action_resubmit,     false);
+        NXAST_ACTION(NXAST_SET_TUNNEL,   struct nx_action_set_tunnel,   false);
+        NXAST_ACTION(NXAST_SET_QUEUE,    struct nx_action_set_queue,    false);
+        NXAST_ACTION(NXAST_POP_QUEUE,    struct nx_action_pop_queue,    false);
+        NXAST_ACTION(NXAST_REG_MOVE,     struct nx_action_reg_move,     false);
+        NXAST_ACTION(NXAST_REG_LOAD,     struct nx_action_reg_load,     false);
+        NXAST_ACTION(NXAST_NOTE,         struct nx_action_note,         true);
+        NXAST_ACTION(NXAST_SET_TUNNEL64, struct nx_action_set_tunnel64, false);
+        NXAST_ACTION(NXAST_MULTIPATH,    struct nx_action_multipath,    false);
+        NXAST_ACTION(NXAST_AUTOPATH,     struct nx_action_autopath,     false);
+        NXAST_ACTION(NXAST_BUNDLE,       struct nx_action_bundle,       true);
+        NXAST_ACTION(NXAST_BUNDLE_LOAD,  struct nx_action_bundle,       true);
+        NXAST_ACTION(NXAST_RESUBMIT_TABLE, struct nx_action_resubmit,   false);
+#undef NXAST_ACTION
 
+    case NXAST_SNAT__OBSOLETE:
+    case NXAST_DROP_SPOOFED_ARP__OBSOLETE:
     default:
-        VLOG_WARN_RL(&bad_ofmsg_rl, "unknown action type %d", (int) type);
-        return ofp_mkerr(OFPET_BAD_ACTION, OFPBAC_BAD_TYPE);
+        return &action_bad_type;
     }
 }
 
+/* Parses 'a' to determine its type.  Returns a nonnegative OFPUTIL_OFPAT_* or
+ * OFPUTIL_NXAST_* constant if successful, otherwise a negative OpenFlow error
+ * code (as returned by ofp_mkerr()).
+ *
+ * The caller must have already verified that 'a''s length is correct (that is,
+ * a->header.len is nonzero and a multiple of sizeof(union ofp_action) and no
+ * longer than the amount of space allocated to 'a').
+ *
+ * This function verifies that 'a''s length is correct for the type of action
+ * that it represents. */
 int
-validate_actions(const union ofp_action *actions, size_t n_actions,
-                 const struct flow *flow, int max_ports)
+ofputil_decode_action(const union ofp_action *a)
 {
-    size_t i;
+    const struct ofputil_action *action;
+    uint16_t len = ntohs(a->header.len);
 
-    for (i = 0; i < n_actions; ) {
-        const union ofp_action *a = &actions[i];
-        unsigned int len = ntohs(a->header.len);
-        unsigned int n_slots = len / OFP_ACTION_ALIGN;
-        unsigned int slots_left = &actions[n_actions] - a;
-        int error;
-
-        if (n_slots > slots_left) {
-            VLOG_WARN_RL(&bad_ofmsg_rl,
-                         "action requires %u slots but only %u remain",
-                         n_slots, slots_left);
-            return ofp_mkerr(OFPET_BAD_ACTION, OFPBAC_BAD_LEN);
-        } else if (!len) {
-            VLOG_WARN_RL(&bad_ofmsg_rl, "action has invalid length 0");
-            return ofp_mkerr(OFPET_BAD_ACTION, OFPBAC_BAD_LEN);
-        } else if (len % OFP_ACTION_ALIGN) {
-            VLOG_WARN_RL(&bad_ofmsg_rl, "action length %u is not a multiple "
-                         "of %d", len, OFP_ACTION_ALIGN);
-            return ofp_mkerr(OFPET_BAD_ACTION, OFPBAC_BAD_LEN);
+    if (a->type != htons(OFPAT_VENDOR)) {
+        action = ofputil_decode_ofpat_action(a);
+    } else {
+        switch (ntohl(a->vendor.vendor)) {
+        case NX_VENDOR_ID:
+            if (len < sizeof(struct nx_action_header)) {
+                return -ofp_mkerr(OFPET_BAD_ACTION, OFPBAC_BAD_LEN);
+            }
+            action = ofputil_decode_nxast_action(a);
+            break;
+        default:
+            action = &action_bad_vendor;
+            break;
         }
+    }
 
-        error = check_action(a, len, flow, max_ports);
-        if (error) {
-            return error;
-        }
-        i += n_slots;
+    return (len >= action->min_len && len <= action->max_len
+            ? action->code
+            : -ofp_mkerr(OFPET_BAD_ACTION, OFPBAC_BAD_LEN));
+}
+
+/* Parses 'a' and returns its type as an OFPUTIL_OFPAT_* or OFPUTIL_NXAST_*
+ * constant.  The caller must have already validated that 'a' is a valid action
+ * understood by Open vSwitch (e.g. by a previous successful call to
+ * ofputil_decode_action()). */
+enum ofputil_action_code
+ofputil_decode_action_unsafe(const union ofp_action *a)
+{
+    const struct ofputil_action *action;
+
+    if (a->type != htons(OFPAT_VENDOR)) {
+        action = ofputil_decode_ofpat_action(a);
+    } else {
+        action = ofputil_decode_nxast_action(a);
     }
-    return 0;
+
+    return action->code;
 }
 
 /* Returns true if 'action' outputs to 'port', false otherwise. */
@@ -2206,30 +2248,6 @@ action_outputs_to_port(const union ofp_action *action, ovs_be16 port)
     }
 }
 
-/* The set of actions must either come from a trusted source or have been
- * previously validated with validate_actions(). */
-const union ofp_action *
-actions_first(struct actions_iterator *iter,
-              const union ofp_action *oa, size_t n_actions)
-{
-    iter->pos = oa;
-    iter->end = oa + n_actions;
-    return actions_next(iter);
-}
-
-const union ofp_action *
-actions_next(struct actions_iterator *iter)
-{
-    if (iter->pos != iter->end) {
-        const union ofp_action *a = iter->pos;
-        unsigned int len = ntohs(a->header.len);
-        iter->pos += len / OFP_ACTION_ALIGN;
-        return a;
-    } else {
-        return NULL;
-    }
-}
-
 /* "Normalizes" the wildcards in 'rule'.  That means:
  *
  *    1. If the type of level N is known, then only the valid fields for that