+static enum ofperr
+ofpacts_from_openflow10(const union ofp_action *in, size_t n_in,
+ struct ofpbuf *out)
+{
+ return ofpacts_from_openflow(in, n_in, out, ofpact_from_openflow10);
+}
+
+static enum ofperr
+ofpacts_pull_actions(struct ofpbuf *openflow, unsigned int actions_len,
+ struct ofpbuf *ofpacts,
+ enum ofperr (*translate)(const union ofp_action *actions,
+ size_t n_actions,
+ struct ofpbuf *ofpacts))
+{
+ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
+ const union ofp_action *actions;
+ enum ofperr error;
+
+ ofpbuf_clear(ofpacts);
+
+ if (actions_len % OFP_ACTION_ALIGN != 0) {
+ VLOG_WARN_RL(&rl, "OpenFlow message actions length %u is not a "
+ "multiple of %d", actions_len, OFP_ACTION_ALIGN);
+ return OFPERR_OFPBRC_BAD_LEN;
+ }
+
+ actions = ofpbuf_try_pull(openflow, actions_len);
+ if (actions == NULL) {
+ VLOG_WARN_RL(&rl, "OpenFlow message actions length %u exceeds "
+ "remaining message length (%zu)",
+ actions_len, openflow->size);
+ return OFPERR_OFPBRC_BAD_LEN;
+ }
+
+ error = translate(actions, actions_len / OFP_ACTION_ALIGN, ofpacts);
+ if (error) {
+ ofpbuf_clear(ofpacts);
+ return error;
+ }
+
+ error = ofpacts_verify(ofpacts->data, ofpacts->size);
+ if (error) {
+ ofpbuf_clear(ofpacts);
+ }
+ return error;
+}
+
+/* Attempts to convert 'actions_len' bytes of OpenFlow 1.0 actions from the
+ * front of 'openflow' into ofpacts. On success, replaces any existing content
+ * in 'ofpacts' by the converted ofpacts; on failure, clears 'ofpacts'.
+ * Returns 0 if successful, otherwise an OpenFlow error.
+ *
+ * The parsed actions are valid generically, but they may not be valid in a
+ * specific context. For example, port numbers up to OFPP_MAX are valid
+ * generically, but specific datapaths may only support port numbers in a
+ * smaller range. Use ofpacts_check() to additional check whether actions are
+ * valid in a specific context. */
+enum ofperr
+ofpacts_pull_openflow10(struct ofpbuf *openflow, unsigned int actions_len,
+ struct ofpbuf *ofpacts)
+{
+ return ofpacts_pull_actions(openflow, actions_len, ofpacts,
+ ofpacts_from_openflow10);
+}
+\f
+/* OpenFlow 1.1 actions. */
+
+/* Parses 'a' to determine its type. On success stores the correct type into
+ * '*code' and returns 0. On failure returns an OFPERR_* error code and
+ * '*code' is indeterminate.
+ *
+ * The caller must have already verified that 'a''s length is potentially
+ * 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. */
+static enum ofperr
+decode_openflow11_action(const union ofp_action *a,
+ enum ofputil_action_code *code)
+{
+ uint16_t len;
+
+ switch (a->type) {
+ case CONSTANT_HTONS(OFPAT11_EXPERIMENTER):
+ return decode_nxast_action(a, code);
+
+#define OFPAT11_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME) \
+ case CONSTANT_HTONS(ENUM): \
+ len = ntohs(a->header.len); \
+ if (EXTENSIBLE \
+ ? len >= sizeof(struct STRUCT) \
+ : len == sizeof(struct STRUCT)) { \
+ *code = OFPUTIL_##ENUM; \
+ return 0; \
+ } else { \
+ return OFPERR_OFPBAC_BAD_LEN; \
+ } \
+ NOT_REACHED();
+#include "ofp-util.def"
+
+ default:
+ return OFPERR_OFPBAC_BAD_TYPE;
+ }
+}
+
+static enum ofperr
+output_from_openflow11(const struct ofp11_action_output *oao,
+ struct ofpbuf *out)
+{
+ struct ofpact_output *output;
+ enum ofperr error;
+
+ output = ofpact_put_OUTPUT(out);
+ output->max_len = ntohs(oao->max_len);
+
+ error = ofputil_port_from_ofp11(oao->port, &output->port);
+ if (error) {
+ return error;
+ }
+
+ return ofputil_check_output_port(output->port, OFPP_MAX);
+}
+
+static enum ofperr
+ofpact_from_openflow11(const union ofp_action *a, struct ofpbuf *out)
+{
+ enum ofputil_action_code code;
+ enum ofperr error;
+
+ error = decode_openflow11_action(a, &code);
+ if (error) {
+ return error;
+ }
+
+ switch (code) {
+ case OFPUTIL_ACTION_INVALID:
+#define OFPAT10_ACTION(ENUM, STRUCT, NAME) case OFPUTIL_##ENUM:
+#include "ofp-util.def"
+ NOT_REACHED();
+
+ case OFPUTIL_OFPAT11_OUTPUT:
+ return output_from_openflow11((const struct ofp11_action_output *) a,
+ out);
+
+ case OFPUTIL_OFPAT11_SET_VLAN_VID:
+ if (a->vlan_vid.vlan_vid & ~htons(0xfff)) {
+ return OFPERR_OFPBAC_BAD_ARGUMENT;
+ }
+ ofpact_put_SET_VLAN_VID(out)->vlan_vid = ntohs(a->vlan_vid.vlan_vid);
+ break;
+
+ case OFPUTIL_OFPAT11_SET_VLAN_PCP:
+ if (a->vlan_pcp.vlan_pcp & ~7) {
+ return OFPERR_OFPBAC_BAD_ARGUMENT;
+ }
+ ofpact_put_SET_VLAN_PCP(out)->vlan_pcp = a->vlan_pcp.vlan_pcp;
+ break;
+
+ case OFPUTIL_OFPAT11_PUSH_VLAN:
+ if (((const struct ofp11_action_push *)a)->ethertype !=
+ htons(ETH_TYPE_VLAN_8021Q)) {
+ /* XXX 802.1AD(QinQ) isn't supported at the moment */
+ return OFPERR_OFPBAC_BAD_ARGUMENT;
+ }
+ ofpact_put_PUSH_VLAN(out);
+ break;
+
+ case OFPUTIL_OFPAT11_POP_VLAN:
+ ofpact_put_STRIP_VLAN(out);
+ break;
+
+ case OFPUTIL_OFPAT11_SET_QUEUE:
+ ofpact_put_SET_QUEUE(out)->queue_id =
+ ntohl(((const struct ofp11_action_set_queue *)a)->queue_id);
+ break;
+
+ case OFPUTIL_OFPAT11_SET_DL_SRC:
+ memcpy(ofpact_put_SET_ETH_SRC(out)->mac,
+ ((const struct ofp_action_dl_addr *) a)->dl_addr, ETH_ADDR_LEN);
+ break;
+
+ case OFPUTIL_OFPAT11_SET_DL_DST:
+ memcpy(ofpact_put_SET_ETH_DST(out)->mac,
+ ((const struct ofp_action_dl_addr *) a)->dl_addr, ETH_ADDR_LEN);
+ break;
+
+ case OFPUTIL_OFPAT11_DEC_NW_TTL:
+ dec_ttl_from_openflow(out, code);
+ break;
+
+ case OFPUTIL_OFPAT11_SET_NW_SRC:
+ ofpact_put_SET_IPV4_SRC(out)->ipv4 = a->nw_addr.nw_addr;
+ break;
+
+ case OFPUTIL_OFPAT11_SET_NW_DST:
+ ofpact_put_SET_IPV4_DST(out)->ipv4 = a->nw_addr.nw_addr;
+ break;
+
+ case OFPUTIL_OFPAT11_SET_NW_TOS:
+ if (a->nw_tos.nw_tos & ~IP_DSCP_MASK) {
+ return OFPERR_OFPBAC_BAD_ARGUMENT;
+ }
+ ofpact_put_SET_IPV4_DSCP(out)->dscp = a->nw_tos.nw_tos;
+ break;
+
+ case OFPUTIL_OFPAT11_SET_TP_SRC:
+ ofpact_put_SET_L4_SRC_PORT(out)->port = ntohs(a->tp_port.tp_port);
+ break;
+
+ case OFPUTIL_OFPAT11_SET_TP_DST:
+ ofpact_put_SET_L4_DST_PORT(out)->port = ntohs(a->tp_port.tp_port);
+ break;
+
+ case OFPUTIL_OFPAT12_SET_FIELD:
+ return nxm_reg_load_from_openflow12_set_field(
+ (const struct ofp12_action_set_field *)a, out);
+
+ case OFPUTIL_OFPAT11_SET_MPLS_TTL: {
+ struct ofp11_action_mpls_ttl *oamt = (struct ofp11_action_mpls_ttl *)a;
+ ofpact_put_SET_MPLS_TTL(out)->ttl = oamt->mpls_ttl;
+ break;
+ }
+
+ case OFPUTIL_OFPAT11_DEC_MPLS_TTL:
+ ofpact_put_DEC_MPLS_TTL(out);
+ break;
+
+ case OFPUTIL_OFPAT11_PUSH_MPLS: {
+ struct ofp11_action_push *oap = (struct ofp11_action_push *)a;
+ if (!eth_type_mpls(oap->ethertype)) {
+ return OFPERR_OFPBAC_BAD_ARGUMENT;
+ }
+ ofpact_put_PUSH_MPLS(out)->ethertype = oap->ethertype;
+ break;
+ }
+
+ case OFPUTIL_OFPAT11_POP_MPLS: {
+ struct ofp11_action_pop_mpls *oapm = (struct ofp11_action_pop_mpls *)a;
+ if (eth_type_mpls(oapm->ethertype)) {
+ return OFPERR_OFPBAC_BAD_ARGUMENT;
+ }
+ ofpact_put_POP_MPLS(out)->ethertype = oapm->ethertype;
+ break;
+ }
+
+#define NXAST_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME) case OFPUTIL_##ENUM:
+#include "ofp-util.def"
+ return ofpact_from_nxast(a, code, out);
+ }
+
+ return error;
+}
+
+static enum ofperr
+ofpacts_from_openflow11(const union ofp_action *in, size_t n_in,
+ struct ofpbuf *out)
+{
+ return ofpacts_from_openflow(in, n_in, out, ofpact_from_openflow11);
+}
+\f
+/* OpenFlow 1.1 instructions. */
+
+#define DEFINE_INST(ENUM, STRUCT, EXTENSIBLE, NAME) \
+ static inline const struct STRUCT * \
+ instruction_get_##ENUM(const struct ofp11_instruction *inst)\
+ { \
+ ovs_assert(inst->type == htons(ENUM)); \
+ return (struct STRUCT *)inst; \
+ } \
+ \
+ static inline void \
+ instruction_init_##ENUM(struct STRUCT *s) \
+ { \
+ memset(s, 0, sizeof *s); \
+ s->type = htons(ENUM); \
+ s->len = htons(sizeof *s); \
+ } \
+ \
+ static inline struct STRUCT * \
+ instruction_put_##ENUM(struct ofpbuf *buf) \
+ { \
+ struct STRUCT *s = ofpbuf_put_uninit(buf, sizeof *s); \
+ instruction_init_##ENUM(s); \
+ return s; \
+ }
+OVS_INSTRUCTIONS
+#undef DEFINE_INST
+
+struct instruction_type_info {
+ enum ovs_instruction_type type;
+ const char *name;
+};
+
+static const struct instruction_type_info inst_info[] = {
+#define DEFINE_INST(ENUM, STRUCT, EXTENSIBLE, NAME) {OVSINST_##ENUM, NAME},
+OVS_INSTRUCTIONS
+#undef DEFINE_INST
+};
+
+const char *
+ofpact_instruction_name_from_type(enum ovs_instruction_type type)
+{
+ return inst_info[type].name;
+}
+
+int
+ofpact_instruction_type_from_name(const char *name)
+{
+ const struct instruction_type_info *p;
+ for (p = inst_info; p < &inst_info[ARRAY_SIZE(inst_info)]; p++) {
+ if (!strcasecmp(name, p->name)) {
+ return p->type;
+ }
+ }
+ return -1;
+}
+
+static inline struct ofp11_instruction *
+instruction_next(const struct ofp11_instruction *inst)
+{
+ return ((struct ofp11_instruction *) (void *)
+ ((uint8_t *) inst + ntohs(inst->len)));
+}
+
+static inline bool
+instruction_is_valid(const struct ofp11_instruction *inst,
+ size_t n_instructions)
+{
+ uint16_t len = ntohs(inst->len);
+ return (!(len % OFP11_INSTRUCTION_ALIGN)
+ && len >= sizeof *inst
+ && len / sizeof *inst <= n_instructions);
+}
+
+/* This macro is careful to check for instructions with bad lengths. */
+#define INSTRUCTION_FOR_EACH(ITER, LEFT, INSTRUCTIONS, N_INSTRUCTIONS) \
+ for ((ITER) = (INSTRUCTIONS), (LEFT) = (N_INSTRUCTIONS); \
+ (LEFT) > 0 && instruction_is_valid(ITER, LEFT); \
+ ((LEFT) -= (ntohs((ITER)->len) \
+ / sizeof(struct ofp11_instruction)), \
+ (ITER) = instruction_next(ITER)))
+
+static enum ofperr
+decode_openflow11_instruction(const struct ofp11_instruction *inst,
+ enum ovs_instruction_type *type)
+{
+ uint16_t len = ntohs(inst->len);
+
+ switch (inst->type) {
+ case CONSTANT_HTONS(OFPIT11_EXPERIMENTER):
+ return OFPERR_OFPBIC_BAD_EXPERIMENTER;
+
+#define DEFINE_INST(ENUM, STRUCT, EXTENSIBLE, NAME) \
+ case CONSTANT_HTONS(ENUM): \
+ if (EXTENSIBLE \
+ ? len >= sizeof(struct STRUCT) \
+ : len == sizeof(struct STRUCT)) { \
+ *type = OVSINST_##ENUM; \
+ return 0; \
+ } else { \
+ return OFPERR_OFPBIC_BAD_LEN; \
+ }
+OVS_INSTRUCTIONS
+#undef DEFINE_INST
+
+ default:
+ return OFPERR_OFPBIC_UNKNOWN_INST;
+ }
+}
+
+static enum ofperr
+decode_openflow11_instructions(const struct ofp11_instruction insts[],
+ size_t n_insts,
+ const struct ofp11_instruction *out[])
+{
+ const struct ofp11_instruction *inst;
+ size_t left;
+
+ memset(out, 0, N_OVS_INSTRUCTIONS * sizeof *out);
+ INSTRUCTION_FOR_EACH (inst, left, insts, n_insts) {
+ enum ovs_instruction_type type;
+ enum ofperr error;
+
+ error = decode_openflow11_instruction(inst, &type);
+ if (error) {
+ return error;
+ }
+
+ if (out[type]) {
+ return OFPERR_OFPBAC_UNSUPPORTED_ORDER; /* No specific code for
+ * a duplicate instruction
+ * exist */
+ }
+ out[type] = inst;
+ }
+
+ if (left) {
+ VLOG_WARN_RL(&rl, "bad instruction format at offset %zu",
+ (n_insts - left) * sizeof *inst);
+ return OFPERR_OFPBIC_BAD_LEN;
+ }
+ return 0;
+}
+
+static void
+get_actions_from_instruction(const struct ofp11_instruction *inst,
+ const union ofp_action **actions,
+ size_t *n_actions)
+{
+ *actions = (const union ofp_action *) (inst + 1);
+ *n_actions = (ntohs(inst->len) - sizeof *inst) / OFP11_INSTRUCTION_ALIGN;
+}
+
+/* Attempts to convert 'actions_len' bytes of OpenFlow 1.1 actions from the
+ * front of 'openflow' into ofpacts. On success, replaces any existing content
+ * in 'ofpacts' by the converted ofpacts; on failure, clears 'ofpacts'.
+ * Returns 0 if successful, otherwise an OpenFlow error.
+ *
+ * In most places in OpenFlow 1.1 and 1.2, actions appear encapsulated in
+ * instructions, so you should call ofpacts_pull_openflow11_instructions()
+ * instead of this function.