oc->reason = nac->reason;
}
+static enum ofperr
+metadata_from_nxast(const struct nx_action_write_metadata *nawm,
+ struct ofpbuf *out)
+{
+ struct ofpact_metadata *om;
+
+ if (!is_all_zeros(nawm->zeros, sizeof nawm->zeros)) {
+ return OFPERR_NXBRC_MUST_BE_ZERO;
+ }
+
+ om = ofpact_put_WRITE_METADATA(out);
+ om->metadata = nawm->metadata;
+ om->mask = nawm->mask;
+
+ return 0;
+}
+
static void
note_from_openflow(const struct nx_action_note *nan, struct ofpbuf *out)
{
const struct nx_action_set_queue *nasq;
const struct nx_action_note *nan;
const struct nx_action_set_tunnel64 *nast64;
+ const struct nx_action_write_metadata *nawm;
struct ofpact_tunnel *tunnel;
enum ofperr error = 0;
tunnel->tun_id = ntohl(nast->tun_id);
break;
+ case OFPUTIL_NXAST_WRITE_METADATA:
+ nawm = (const struct nx_action_write_metadata *) a;
+ error = metadata_from_nxast(nawm, out);
+ break;
+
case OFPUTIL_NXAST_SET_QUEUE:
nasq = (const struct nx_action_set_queue *) a;
ofpact_put_SET_QUEUE(out)->queue_id = ntohl(nasq->queue_id);
}
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);
}
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)) {
+ /* TODO:XXX 802.1AD(QinQ) isn't supported at the moment */
+ return OFPERR_OFPET_BAD_ACTION;
+ }
+ ofpact_put_PUSH_VLAN(out);
+ break;
+
case OFPUTIL_OFPAT11_POP_VLAN:
ofpact_put_STRIP_VLAN(out);
break;
ofpact_put_CLEAR_ACTIONS(ofpacts);
}
/* TODO:XXX Write-Actions */
- /* TODO:XXX Write-Metadata */
+ if (insts[OVSINST_OFPIT11_WRITE_METADATA]) {
+ const struct ofp11_instruction_write_metadata *oiwm;
+ struct ofpact_metadata *om;
+
+ oiwm = (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;
ogt->table_id = oigt->table_id;
}
- if (insts[OVSINST_OFPIT11_WRITE_METADATA] ||
- insts[OVSINST_OFPIT11_WRITE_ACTIONS]) {
+ 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);
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:
return 0;
case OFPACT_CLEAR_ACTIONS:
+ case OFPACT_WRITE_METADATA:
case OFPACT_GOTO_TABLE:
return 0;
return 0;
}
+
+/* Verifies that the 'ofpacts_len' bytes of actions in 'ofpacts' are
+ * in the appropriate order as defined by the OpenFlow spec. */
+enum ofperr
+ofpacts_verify(const struct ofpact ofpacts[], size_t ofpacts_len)
+{
+ const struct ofpact *a;
+ const struct ofpact_metadata *om = NULL;
+
+ OFPACT_FOR_EACH (a, ofpacts, ofpacts_len) {
+ if (om) {
+ if (a->type == OFPACT_WRITE_METADATA) {
+ VLOG_WARN("duplicate write_metadata instruction specified");
+ /* should be OFPERR_OFPET_BAD_ACTION? */
+ return OFPERR_OFPBAC_UNSUPPORTED_ORDER;
+ } else {
+ VLOG_WARN("write_metadata instruction must be specified after "
+ "other instructions/actions");
+ return OFPERR_OFPBAC_UNSUPPORTED_ORDER;
+ }
+ }
+
+ if (a->type == OFPACT_WRITE_METADATA) {
+ om = (const struct ofpact_metadata *) a;
+ }
+ }
+
+ return 0;
+}
\f
/* Converting ofpacts to Nicira OpenFlow extensions. */
}
}
+static void
+ofpact_write_metadata_to_nxast(const struct ofpact_metadata *om,
+ struct ofpbuf *out)
+{
+ struct nx_action_write_metadata *nawm;
+
+ nawm = ofputil_put_NXAST_WRITE_METADATA(out);
+ nawm->metadata = om->metadata;
+ nawm->mask = om->mask;
+}
+
static void
ofpact_note_to_nxast(const struct ofpact_note *note, struct ofpbuf *out)
{
ofpact_set_tunnel_to_nxast(ofpact_get_SET_TUNNEL(a), out);
break;
+ case OFPACT_WRITE_METADATA:
+ ofpact_write_metadata_to_nxast(ofpact_get_WRITE_METADATA(a), out);
+ break;
+
case OFPACT_SET_QUEUE:
ofputil_put_NXAST_SET_QUEUE(out)->queue_id
= htonl(ofpact_get_SET_QUEUE(a)->queue_id);
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:
= htons(ofpact_get_SET_L4_DST_PORT(a)->port);
break;
+ case OFPACT_PUSH_VLAN:
case OFPACT_CLEAR_ACTIONS:
case OFPACT_GOTO_TABLE:
/* TODO:XXX */
case OFPACT_REG_LOAD:
case OFPACT_DEC_TTL:
case OFPACT_SET_TUNNEL:
+ case OFPACT_WRITE_METADATA:
case OFPACT_SET_QUEUE:
case OFPACT_POP_QUEUE:
case OFPACT_FIN_TIMEOUT:
ofputil_put_OFPAT11_POP_VLAN(out);
break;
+ case OFPACT_PUSH_VLAN:
+ /* TODO:XXX ETH_TYPE_VLAN_8021AD case */
+ ofputil_put_OFPAT11_PUSH_VLAN(out)->ethertype =
+ htons(ETH_TYPE_VLAN_8021Q);
+ break;
+
case OFPACT_SET_ETH_SRC:
memcpy(ofputil_put_OFPAT11_SET_DL_SRC(out)->dl_addr,
ofpact_get_SET_ETH_SRC(a)->mac, ETH_ADDR_LEN);
ofpact_dec_ttl_to_openflow11(ofpact_get_DEC_TTL(a), out);
break;
+ case OFPACT_WRITE_METADATA:
+ /* OpenFlow 1.1 uses OFPIT_WRITE_METADATA to express this action. */
+ break;
+
case OFPACT_CLEAR_ACTIONS:
case OFPACT_GOTO_TABLE:
NOT_REACHED();
OFPACT_FOR_EACH (a, ofpacts, ofpacts_len) {
/* TODO:XXX Write-Actions */
- /* TODO:XXX Write-Metadata */
+
if (a->type == OFPACT_CLEAR_ACTIONS) {
instruction_put_OFPIT11_CLEAR_ACTIONS(openflow);
} else if (a->type == OFPACT_GOTO_TABLE) {
oigt = instruction_put_OFPIT11_GOTO_TABLE(openflow);
oigt->table_id = ofpact_get_GOTO_TABLE(a)->table_id;
memset(oigt->pad, 0, sizeof oigt->pad);
+ } else if (a->type == OFPACT_WRITE_METADATA) {
+ const struct ofpact_metadata *om;
+ struct ofp11_instruction_write_metadata *oiwm;
+
+ om = ofpact_get_WRITE_METADATA(a);
+ oiwm = instruction_put_OFPIT11_WRITE_METADATA(openflow);
+ oiwm->metadata = om->metadata;
+ oiwm->metadata_mask = om->mask;
} else if (!ofpact_is_instruction(a)) {
/* Apply-actions */
const size_t ofs = openflow->size;
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_REG_LOAD:
case OFPACT_DEC_TTL:
case OFPACT_SET_TUNNEL:
+ case OFPACT_WRITE_METADATA:
case OFPACT_SET_QUEUE:
case OFPACT_POP_QUEUE:
case OFPACT_FIN_TIMEOUT:
const struct ofpact_resubmit *resubmit;
const struct ofpact_autopath *autopath;
const struct ofpact_controller *controller;
+ const struct ofpact_metadata *metadata;
const struct ofpact_tunnel *tunnel;
uint16_t port;
ds_put_cstr(s, "strip_vlan");
break;
+ case OFPACT_PUSH_VLAN:
+ /* TODO:XXX 802.1AD case*/
+ ds_put_format(s, "push_vlan:%#"PRIx16, ETH_TYPE_VLAN_8021Q);
+ break;
+
case OFPACT_SET_ETH_SRC:
ds_put_format(s, "mod_dl_src:"ETH_ADDR_FMT,
ETH_ADDR_ARGS(ofpact_get_SET_ETH_SRC(a)->mac));
OVSINST_OFPIT11_CLEAR_ACTIONS));
break;
+ case OFPACT_WRITE_METADATA:
+ metadata = ofpact_get_WRITE_METADATA(a);
+ ds_put_format(s, "%s:%#"PRIx64,
+ ofpact_instruction_name_from_type(
+ OVSINST_OFPIT11_WRITE_METADATA),
+ ntohll(metadata->metadata));
+ if (metadata->mask != htonll(UINT64_MAX)) {
+ ds_put_format(s, "/%#"PRIx64, ntohll(metadata->mask));
+ }
+ break;
+
case OFPACT_GOTO_TABLE:
ds_put_format(s, "%s:%"PRIu8,
ofpact_instruction_name_from_type(
}
/* TODO:XXX write-actions */
- /* TODO:XXX write-metadata */
ofpact_format(a, string);
}
}