/*
- * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
#include "nx-match.h"
#include "ofp-util.h"
#include "ofpbuf.h"
+#include "util.h"
#include "vlog.h"
VLOG_DEFINE_THIS_MODULE(ofp_actions);
}
static enum ofperr
-enqueue_from_openflow10(const struct ofp_action_enqueue *oae,
+enqueue_from_openflow10(const struct ofp10_action_enqueue *oae,
struct ofpbuf *out)
{
struct ofpact_enqueue *enqueue;
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)
{
}
static enum ofperr
-dec_ttl_from_openflow(struct ofpbuf *out)
+dec_ttl_from_openflow(struct ofpbuf *out, enum ofputil_action_code compat)
{
uint16_t id = 0;
struct ofpact_cnt_ids *ids;
enum ofperr error = 0;
ids = ofpact_put_DEC_TTL(out);
- ids->ofpact.compat = OFPUTIL_NXAST_DEC_TTL;
+ ids->ofpact.compat = compat;
ids->n_controllers = 1;
ofpbuf_put(out, &id, sizeof id);
ids = out->l2;
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);
break;
case OFPUTIL_NXAST_DEC_TTL:
- error = dec_ttl_from_openflow(out);
+ error = dec_ttl_from_openflow(out, code);
break;
case OFPUTIL_NXAST_DEC_TTL_CNT_IDS:
break;
case OFPUTIL_OFPAT10_ENQUEUE:
- error = enqueue_from_openflow10((const struct ofp_action_enqueue *) a,
+ error = enqueue_from_openflow10((const struct ofp10_action_enqueue *) a,
out);
break;
}
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)) {
+ /* 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);
((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;
static inline const struct STRUCT * \
instruction_get_##ENUM(const struct ofp11_instruction *inst)\
{ \
- assert(inst->type == htons(ENUM)); \
+ ovs_assert(inst->type == htons(ENUM)); \
return (struct STRUCT *)inst; \
} \
\
}
if (out[type]) {
- return OFPERR_OFPIT_BAD_INSTRUCTION;
+ return OFPERR_OFPBAC_UNSUPPORTED_ORDER; /* No specific code for
+ * a duplicate instruction
+ * exist */
}
out[type] = inst;
}
insts[OVSINST_OFPIT11_CLEAR_ACTIONS]);
ofpact_put_CLEAR_ACTIONS(ofpacts);
}
- /* TODO:XXX Write-Actions */
- /* TODO:XXX Write-Metadata */
+ /* XXX Write-Actions */
+ 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;
+ enum ovs_instruction_type inst;
+
+ inst = OVSINST_OFPIT11_APPLY_ACTIONS;
+ OFPACT_FOR_EACH (a, ofpacts, ofpacts_len) {
+ enum ovs_instruction_type next;
+
+ if (a->type == OFPACT_CLEAR_ACTIONS) {
+ next = OVSINST_OFPIT11_CLEAR_ACTIONS;
+ } else if (a->type == OFPACT_WRITE_METADATA) {
+ next = OVSINST_OFPIT11_WRITE_METADATA;
+ } else if (a->type == OFPACT_GOTO_TABLE) {
+ next = OVSINST_OFPIT11_GOTO_TABLE;
+ } else {
+ next = OVSINST_OFPIT11_APPLY_ACTIONS;
+ }
+
+ if (inst != OVSINST_OFPIT11_APPLY_ACTIONS && next <= inst) {
+ const char *name = ofpact_instruction_name_from_type(inst);
+ const char *next_name = ofpact_instruction_name_from_type(next);
+
+ if (next == inst) {
+ VLOG_WARN("duplicate %s instruction not allowed, for OpenFlow "
+ "1.1+ compatibility", name);
+ } else {
+ VLOG_WARN("invalid instruction ordering: %s must appear "
+ "before %s, for OpenFlow 1.1+ compatibility",
+ next_name, name);
+ }
+ return OFPERR_OFPBAC_UNSUPPORTED_ORDER;
+ }
+
+ inst = next;
+ }
+
+ 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:
ofpact_enqueue_to_openflow10(const struct ofpact_enqueue *enqueue,
struct ofpbuf *out)
{
- struct ofp_action_enqueue *oae;
+ struct ofp10_action_enqueue *oae;
oae = ofputil_put_OFPAT10_ENQUEUE(out);
oae->port = htons(enqueue->port);
= htons(ofpact_get_SET_L4_DST_PORT(a)->port);
break;
+ case OFPACT_PUSH_VLAN:
case OFPACT_CLEAR_ACTIONS:
case OFPACT_GOTO_TABLE:
- /* TODO:XXX */
+ /* XXX */
break;
case OFPACT_CONTROLLER:
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:
oao->max_len = htons(output->max_len);
}
+static void
+ofpact_dec_ttl_to_openflow11(const struct ofpact_cnt_ids *dec_ttl,
+ struct ofpbuf *out)
+{
+ if (dec_ttl->n_controllers == 1 && dec_ttl->cnt_ids[0] == 0
+ && (!dec_ttl->ofpact.compat ||
+ dec_ttl->ofpact.compat == OFPUTIL_OFPAT11_DEC_NW_TTL)) {
+ ofputil_put_OFPAT11_DEC_NW_TTL(out);
+ } else {
+ ofpact_dec_ttl_to_nxast(dec_ttl, out);
+ }
+}
+
static void
ofpact_to_openflow11(const struct ofpact *a, struct ofpbuf *out)
{
break;
case OFPACT_STRIP_VLAN:
- /* XXX */
+ ofputil_put_OFPAT11_POP_VLAN(out);
+ break;
+
+ case OFPACT_PUSH_VLAN:
+ /* XXX ETH_TYPE_VLAN_8021AD case */
+ ofputil_put_OFPAT11_PUSH_VLAN(out)->ethertype =
+ htons(ETH_TYPE_VLAN_8021Q);
+ break;
+
+ case OFPACT_SET_QUEUE:
+ ofputil_put_OFPAT11_SET_QUEUE(out)->queue_id
+ = htonl(ofpact_get_SET_QUEUE(a)->queue_id);
break;
case OFPACT_SET_ETH_SRC:
= htons(ofpact_get_SET_L4_DST_PORT(a)->port);
break;
+ case OFPACT_DEC_TTL:
+ 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();
case OFPACT_BUNDLE:
case OFPACT_REG_MOVE:
case OFPACT_REG_LOAD:
- case OFPACT_DEC_TTL:
case OFPACT_SET_TUNNEL:
- case OFPACT_SET_QUEUE:
case OFPACT_POP_QUEUE:
case OFPACT_FIN_TIMEOUT:
case OFPACT_RESUBMIT:
const struct ofpact *a;
OFPACT_FOR_EACH (a, ofpacts, ofpacts_len) {
- /* TODO:XXX Write-Actions */
- /* TODO:XXX Write-Metadata */
+ /* XXX Write-Actions */
+
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:
+ /* 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));
case OFPACT_SET_IPV4_SRC:
ds_put_format(s, "mod_nw_src:"IP_FMT,
- IP_ARGS(&ofpact_get_SET_IPV4_SRC(a)->ipv4));
+ IP_ARGS(ofpact_get_SET_IPV4_SRC(a)->ipv4));
break;
case OFPACT_SET_IPV4_DST:
ds_put_format(s, "mod_nw_dst:"IP_FMT,
- IP_ARGS(&ofpact_get_SET_IPV4_DST(a)->ipv4));
+ IP_ARGS(ofpact_get_SET_IPV4_DST(a)->ipv4));
break;
case OFPACT_SET_IPV4_DSCP:
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(
ds_put_cstr(string, ",");
}
- /* TODO:XXX write-actions */
- /* TODO:XXX write-metadata */
+ /* XXX write-actions */
ofpact_format(a, string);
}
}
void
ofpact_update_len(struct ofpbuf *ofpacts, struct ofpact *ofpact)
{
- assert(ofpact == ofpacts->l2);
+ ovs_assert(ofpact == ofpacts->l2);
ofpact->len = (char *) ofpbuf_tail(ofpacts) - (char *) ofpact;
}