+ 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 ALIGNED_CAST(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 *
+ovs_instruction_name_from_type(enum ovs_instruction_type type)
+{
+ return inst_info[type].name;
+}
+
+int
+ovs_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;
+}
+
+enum ovs_instruction_type
+ovs_instruction_type_from_ofpact_type(enum ofpact_type type)
+{
+ switch (type) {
+ case OFPACT_METER:
+ return OVSINST_OFPIT13_METER;
+ case OFPACT_CLEAR_ACTIONS:
+ return OVSINST_OFPIT11_CLEAR_ACTIONS;
+ case OFPACT_WRITE_METADATA:
+ return OVSINST_OFPIT11_WRITE_METADATA;
+ case OFPACT_GOTO_TABLE:
+ return OVSINST_OFPIT11_GOTO_TABLE;
+ case OFPACT_OUTPUT:
+ case OFPACT_CONTROLLER:
+ case OFPACT_ENQUEUE:
+ case OFPACT_OUTPUT_REG:
+ case OFPACT_BUNDLE:
+ case OFPACT_SET_VLAN_VID:
+ case OFPACT_SET_VLAN_PCP:
+ case OFPACT_STRIP_VLAN:
+ case OFPACT_PUSH_VLAN:
+ case OFPACT_SET_ETH_SRC:
+ case OFPACT_SET_ETH_DST:
+ case OFPACT_SET_IPV4_SRC:
+ case OFPACT_SET_IPV4_DST:
+ case OFPACT_SET_IPV4_DSCP:
+ case OFPACT_SET_L4_SRC_PORT:
+ case OFPACT_SET_L4_DST_PORT:
+ case OFPACT_REG_MOVE:
+ case OFPACT_REG_LOAD:
+ case OFPACT_STACK_PUSH:
+ case OFPACT_STACK_POP:
+ case OFPACT_DEC_TTL:
+ case OFPACT_SET_MPLS_TTL:
+ case OFPACT_DEC_MPLS_TTL:
+ case OFPACT_PUSH_MPLS:
+ case OFPACT_POP_MPLS:
+ case OFPACT_SET_TUNNEL:
+ case OFPACT_SET_QUEUE:
+ case OFPACT_POP_QUEUE:
+ case OFPACT_FIN_TIMEOUT:
+ case OFPACT_RESUBMIT:
+ case OFPACT_LEARN:
+ case OFPACT_MULTIPATH:
+ case OFPACT_NOTE:
+ case OFPACT_EXIT:
+ case OFPACT_SAMPLE:
+ default:
+ return OVSINST_OFPIT11_APPLY_ACTIONS;
+ }
+}
+
+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_ONFBIC_DUP_INSTRUCTION;
+ }
+ 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 = ALIGNED_CAST(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.
+ *
+ * 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_openflow11_actions(struct ofpbuf *openflow,
+ unsigned int actions_len,
+ struct ofpbuf *ofpacts)
+{
+ return ofpacts_pull_actions(openflow, actions_len, ofpacts,
+ ofpacts_from_openflow11);
+}
+
+enum ofperr
+ofpacts_pull_openflow11_instructions(struct ofpbuf *openflow,
+ unsigned int instructions_len,
+ struct ofpbuf *ofpacts)
+{
+ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
+ const struct ofp11_instruction *instructions;
+ const struct ofp11_instruction *insts[N_OVS_INSTRUCTIONS];
+ enum ofperr error;
+
+ ofpbuf_clear(ofpacts);
+
+ if (instructions_len % OFP11_INSTRUCTION_ALIGN != 0) {
+ VLOG_WARN_RL(&rl, "OpenFlow message instructions length %u is not a "
+ "multiple of %d",
+ instructions_len, OFP11_INSTRUCTION_ALIGN);
+ error = OFPERR_OFPBIC_BAD_LEN;
+ goto exit;
+ }
+
+ instructions = ofpbuf_try_pull(openflow, instructions_len);
+ if (instructions == NULL) {
+ VLOG_WARN_RL(&rl, "OpenFlow message instructions length %u exceeds "
+ "remaining message length (%zu)",
+ instructions_len, openflow->size);
+ error = OFPERR_OFPBIC_BAD_LEN;
+ goto exit;
+ }
+
+ error = decode_openflow11_instructions(
+ instructions, instructions_len / OFP11_INSTRUCTION_ALIGN,
+ insts);
+ if (error) {
+ goto exit;
+ }
+
+ if (insts[OVSINST_OFPIT13_METER]) {
+ const struct ofp13_instruction_meter *oim;
+ struct ofpact_meter *om;
+
+ oim = ALIGNED_CAST(const struct ofp13_instruction_meter *,
+ insts[OVSINST_OFPIT13_METER]);
+
+ om = ofpact_put_METER(ofpacts);
+ om->meter_id = ntohl(oim->meter_id);
+ }
+ if (insts[OVSINST_OFPIT11_APPLY_ACTIONS]) {
+ const union ofp_action *actions;
+ size_t n_actions;
+
+ get_actions_from_instruction(insts[OVSINST_OFPIT11_APPLY_ACTIONS],
+ &actions, &n_actions);
+ error = ofpacts_from_openflow11(actions, n_actions, ofpacts);
+ if (error) {
+ goto exit;
+ }
+ }
+ if (insts[OVSINST_OFPIT11_CLEAR_ACTIONS]) {
+ instruction_get_OFPIT11_CLEAR_ACTIONS(
+ insts[OVSINST_OFPIT11_CLEAR_ACTIONS]);
+ ofpact_put_CLEAR_ACTIONS(ofpacts);
+ }
+ /* XXX Write-Actions */
+ if (insts[OVSINST_OFPIT11_WRITE_METADATA]) {
+ const struct ofp11_instruction_write_metadata *oiwm;
+ struct ofpact_metadata *om;
+
+ oiwm = ALIGNED_CAST(const struct ofp11_instruction_write_metadata *,
+ insts[OVSINST_OFPIT11_WRITE_METADATA]);
+
+ om = ofpact_put_WRITE_METADATA(ofpacts);
+ om->metadata = oiwm->metadata;
+ om->mask = oiwm->metadata_mask;
+ }
+ if (insts[OVSINST_OFPIT11_GOTO_TABLE]) {
+ const struct ofp11_instruction_goto_table *oigt;
+ struct ofpact_goto_table *ogt;
+
+ oigt = instruction_get_OFPIT11_GOTO_TABLE(
+ insts[OVSINST_OFPIT11_GOTO_TABLE]);
+ ogt = ofpact_put_GOTO_TABLE(ofpacts);
+ ogt->table_id = oigt->table_id;
+ }
+
+ if (insts[OVSINST_OFPIT11_WRITE_ACTIONS]) {
+ error = OFPERR_OFPBIC_UNSUP_INST;
+ goto exit;
+ }
+
+ error = ofpacts_verify(ofpacts->data, ofpacts->size);
+exit:
+ if (error) {
+ ofpbuf_clear(ofpacts);
+ }
+ return error;
+}
+\f
+/* May modify flow->dl_type, caller must restore it. */
+static enum ofperr
+ofpact_check__(const struct ofpact *a, struct flow *flow, ofp_port_t max_ports,
+ uint8_t table_id)
+{
+ const struct ofpact_enqueue *enqueue;
+
+ switch (a->type) {
+ case OFPACT_OUTPUT:
+ return ofputil_check_output_port(ofpact_get_OUTPUT(a)->port,
+ max_ports);