#include "bundle.h"
#include "byte-order.h"
#include "compiler.h"
+#include "dummy.h"
#include "dynamic-string.h"
#include "hmap.h"
#include "learn.h"
/* NX1.0+(34): struct nx_action_conjunction. */
NXAST_RAW_CONJUNCTION,
+
+/* ## ------------------ ## */
+/* ## Debugging actions. ## */
+/* ## ------------------ ## */
+
+/* These are intentionally undocumented, subject to change, and ovs-vswitchd */
+/* accepts them only if started with --enable-dummy. */
+
+ /* NX1.0+(255): void. */
+ NXAST_RAW_DEBUG_RECIRC,
};
/* OpenFlow actions are always a multiple of 8 bytes in length. */
static void pad_ofpat(struct ofpbuf *openflow, size_t start_ofs);
static enum ofperr ofpacts_verify(const struct ofpact[], size_t ofpacts_len,
- uint32_t allowed_ovsinsts);
+ uint32_t allowed_ovsinsts,
+ enum ofpact_type outer_action);
static void ofpact_put_set_field(struct ofpbuf *openflow, enum ofp_version,
enum mf_field_id, uint64_t value);
static char *OVS_WARN_UNUSED_RESULT ofpacts_parse(
char *str, struct ofpbuf *ofpacts, enum ofputil_protocol *usable_protocols,
- bool allow_instructions);
+ bool allow_instructions, enum ofpact_type outer_action);
+
+/* Pull off existing actions or instructions. Used by nesting actions to keep
+ * ofpacts_parse() oblivious of actions nesting.
+ *
+ * Push the actions back on after nested parsing, e.g.:
+ *
+ * size_t ofs = ofpacts_pull(ofpacts);
+ * ...nested parsing...
+ * ofpbuf_push_uninit(ofpacts, ofs);
+ */
+static size_t
+ofpacts_pull(struct ofpbuf *ofpacts)
+{
+ size_t ofs;
+
+ ofpact_pad(ofpacts);
+ ofs = ofpacts->size;
+ ofpbuf_pull(ofpacts, ofs);
+
+ return ofs;
+}
#include "ofp-actions.inc1"
\f
struct ofp_action_dl_addr {
ovs_be16 type; /* Type. */
ovs_be16 len; /* Length is 16. */
- uint8_t dl_addr[OFP_ETH_ALEN]; /* Ethernet address. */
+ struct eth_addr dl_addr; /* Ethernet address. */
uint8_t pad[6];
};
OFP_ASSERT(sizeof(struct ofp_action_dl_addr) == 16);
decode_OFPAT_RAW_SET_DL_SRC(const struct ofp_action_dl_addr *a,
struct ofpbuf *out)
{
- memcpy(ofpact_put_SET_ETH_SRC(out)->mac, a->dl_addr, ETH_ADDR_LEN);
+ ofpact_put_SET_ETH_SRC(out)->mac = a->dl_addr;
return 0;
}
decode_OFPAT_RAW_SET_DL_DST(const struct ofp_action_dl_addr *a,
struct ofpbuf *out)
{
- memcpy(ofpact_put_SET_ETH_DST(out)->mac, a->dl_addr, ETH_ADDR_LEN);
+ ofpact_put_SET_ETH_DST(out)->mac = a->dl_addr;
return 0;
}
enum ofp_raw_action_type raw, enum mf_field_id field,
struct ofpbuf *out)
{
- const uint8_t *addr = mac->mac;
-
if (ofp_version < OFP12_VERSION) {
struct ofp_action_dl_addr *oada;
oada = ofpact_put_raw(out, ofp_version, raw, 0);
- memcpy(oada->dl_addr, addr, ETH_ADDR_LEN);
+ oada->dl_addr = mac->mac;
} else {
ofpact_put_set_field(out, ofp_version, field,
- eth_addr_to_uint64(addr));
+ eth_addr_to_uint64(mac->mac));
}
}
parse_SET_ETH_SRC(char *arg, struct ofpbuf *ofpacts,
enum ofputil_protocol *usable_protocols OVS_UNUSED)
{
- return str_to_mac(arg, ofpact_put_SET_ETH_SRC(ofpacts)->mac);
+ return str_to_mac(arg, &ofpact_put_SET_ETH_SRC(ofpacts)->mac);
}
static char * OVS_WARN_UNUSED_RESULT
parse_SET_ETH_DST(char *arg, struct ofpbuf *ofpacts,
enum ofputil_protocol *usable_protocols OVS_UNUSED)
{
- return str_to_mac(arg, ofpact_put_SET_ETH_DST(ofpacts)->mac);
+ return str_to_mac(arg, &ofpact_put_SET_ETH_DST(ofpacts)->mac);
}
static void
set_field_to_nxast(const struct ofpact_set_field *sf, struct ofpbuf *openflow)
{
/* If 'sf' cannot be encoded as NXAST_REG_LOAD because it requires an
- * experimenter OXM (or if it came in as NXAST_REG_LOAD2), encode as
- * NXAST_REG_LOAD2. Otherwise use NXAST_REG_LOAD, which is backward
- * compatible. */
+ * experimenter OXM or is variable length (or if it came in as
+ * NXAST_REG_LOAD2), encode as NXAST_REG_LOAD2. Otherwise use
+ * NXAST_REG_LOAD, which is backward compatible. */
if (sf->ofpact.raw == NXAST_RAW_REG_LOAD2
- || !mf_nxm_header(sf->field->id)) {
+ || !mf_nxm_header(sf->field->id) || sf->field->variable_len) {
struct nx_action_reg_load2 *narl OVS_UNUSED;
size_t start_ofs = openflow->size;
break;
case MFF_ETH_SRC:
- memcpy(put_OFPAT_SET_DL_SRC(out, ofp_version)->dl_addr,
- sf->value.mac, ETH_ADDR_LEN);
+ put_OFPAT_SET_DL_SRC(out, ofp_version)->dl_addr = sf->value.mac;
break;
case MFF_ETH_DST:
- memcpy(put_OFPAT_SET_DL_DST(out, ofp_version)->dl_addr,
- sf->value.mac, ETH_ADDR_LEN);
+ put_OFPAT_SET_DL_DST(out, ofp_version)->dl_addr = sf->value.mac;
break;
case MFF_IPV4_SRC:
* address. This is not usually the intent in MAC learning; instead, we want
* the MAC learn entry to expire when no traffic has been sent *from* the
* learned address. Use a hard timeout for that.
- */
+ *
+ *
+ * Visibility of Changes
+ * ---------------------
+ *
+ * Prior to Open vSwitch 2.4, any changes made by a "learn" action in a given
+ * flow translation are visible to flow table lookups made later in the flow
+ * translation. This means that, in the example above, a MAC learned by the
+ * learn action in table 0 would be found in table 1 (if the packet being
+ * processed had the same source and destination MAC address).
+ *
+ * In Open vSwitch 2.4 and later, changes to a flow table (whether to add or
+ * modify a flow) by a "learn" action are visible only for later flow
+ * translations, not for later lookups within the same flow translation. In
+ * the MAC learning example, a MAC learned by the learn action in table 0 would
+ * not be found in table 1 if the flow translation would resubmit to table 1
+ * after the processing of the learn action, meaning that if this MAC had not
+ * been learned before then the packet would be flooded. */
struct nx_action_learn {
ovs_be16 type; /* OFPAT_VENDOR. */
ovs_be16 len; /* At least 24. */
ds_put_cstr(s, "exit");
}
\f
+/* Unroll xlate action. */
+
+static void
+encode_UNROLL_XLATE(const struct ofpact_unroll_xlate *unroll OVS_UNUSED,
+ enum ofp_version ofp_version OVS_UNUSED,
+ struct ofpbuf *out OVS_UNUSED)
+{
+ OVS_NOT_REACHED();
+}
+
+static char * OVS_WARN_UNUSED_RESULT
+parse_UNROLL_XLATE(char *arg OVS_UNUSED, struct ofpbuf *ofpacts OVS_UNUSED,
+ enum ofputil_protocol *usable_protocols OVS_UNUSED)
+{
+ OVS_NOT_REACHED();
+ return NULL;
+}
+
+static void
+format_UNROLL_XLATE(const struct ofpact_unroll_xlate *a OVS_UNUSED,
+ struct ds *s)
+{
+ ds_put_cstr(s, "unroll_xlate");
+}
+\f
/* Action structure for NXAST_SAMPLE.
*
* Samples matching packets with the given probability and sends them
a->obs_domain_id, a->obs_point_id);
}
\f
+/* debug_recirc instruction. */
+
+static bool enable_debug;
+
+void
+ofpact_dummy_enable(void)
+{
+ enable_debug = true;
+}
+
+static enum ofperr
+decode_NXAST_RAW_DEBUG_RECIRC(struct ofpbuf *out)
+{
+ if (!enable_debug) {
+ return OFPERR_OFPBAC_BAD_VENDOR_TYPE;
+ }
+
+ ofpact_put_DEBUG_RECIRC(out);
+ return 0;
+}
+
+static void
+encode_DEBUG_RECIRC(const struct ofpact_null *n OVS_UNUSED,
+ enum ofp_version ofp_version OVS_UNUSED,
+ struct ofpbuf *out)
+{
+ put_NXAST_DEBUG_RECIRC(out);
+}
+
+static char * OVS_WARN_UNUSED_RESULT
+parse_DEBUG_RECIRC(char *arg OVS_UNUSED, struct ofpbuf *ofpacts,
+ enum ofputil_protocol *usable_protocols OVS_UNUSED)
+{
+ ofpact_put_DEBUG_RECIRC(ofpacts);
+ return NULL;
+}
+
+static void
+format_DEBUG_RECIRC(const struct ofpact_null *a OVS_UNUSED, struct ds *s)
+{
+ ds_put_cstr(s, "debug_recirc");
+}
+\f
/* Meter instruction. */
static void
parse_WRITE_ACTIONS(char *arg, struct ofpbuf *ofpacts,
enum ofputil_protocol *usable_protocols)
{
+ size_t ofs = ofpacts_pull(ofpacts);
struct ofpact_nest *on;
char *error;
- size_t ofs;
-
- /* Pull off existing actions or instructions. */
- ofpact_pad(ofpacts);
- ofs = ofpacts->size;
- ofpbuf_pull(ofpacts, ofs);
/* Add a Write-Actions instruction and then pull it off. */
ofpact_put(ofpacts, OFPACT_WRITE_ACTIONS, sizeof *on);
* that it doesn't actually include the nested actions. That means that
* ofpacts_parse() would reject them as being part of an Apply-Actions that
* follows a Write-Actions, which is an invalid order. */
- error = ofpacts_parse(arg, ofpacts, usable_protocols, false);
+ error = ofpacts_parse(arg, ofpacts, usable_protocols, false,
+ OFPACT_WRITE_ACTIONS);
/* Put the Write-Actions back on and update its length. */
on = ofpbuf_push_uninit(ofpacts, sizeof *on);
unsigned int actions_len,
enum ofp_version version,
uint32_t allowed_ovsinsts,
- struct ofpbuf *ofpacts)
+ struct ofpbuf *ofpacts,
+ enum ofpact_type outer_action)
{
const struct ofp_action_header *actions;
enum ofperr error;
- ofpbuf_clear(ofpacts);
+ if (!outer_action) {
+ ofpbuf_clear(ofpacts);
+ }
if (actions_len % OFP_ACTION_ALIGN != 0) {
VLOG_WARN_RL(&rl, "OpenFlow message actions length %u is not a "
return error;
}
- error = ofpacts_verify(ofpacts->data, ofpacts->size,
- allowed_ovsinsts);
+ error = ofpacts_verify(ofpacts->data, ofpacts->size, allowed_ovsinsts,
+ outer_action);
if (error) {
ofpbuf_clear(ofpacts);
}
{
return ofpacts_pull_openflow_actions__(openflow, actions_len, version,
1u << OVSINST_OFPIT11_APPLY_ACTIONS,
- ofpacts);
+ ofpacts, 0);
}
\f
/* OpenFlow 1.1 actions. */
case OFPACT_DEC_TTL:
case OFPACT_ENQUEUE:
case OFPACT_EXIT:
+ case OFPACT_UNROLL_XLATE:
case OFPACT_FIN_TIMEOUT:
case OFPACT_GOTO_TABLE:
case OFPACT_GROUP:
case OFPACT_STRIP_VLAN:
case OFPACT_WRITE_ACTIONS:
case OFPACT_WRITE_METADATA:
+ case OFPACT_DEBUG_RECIRC:
return false;
default:
OVS_NOT_REACHED();
case OFPACT_CONTROLLER:
case OFPACT_ENQUEUE:
case OFPACT_EXIT:
+ case OFPACT_UNROLL_XLATE:
case OFPACT_FIN_TIMEOUT:
case OFPACT_LEARN:
case OFPACT_CONJUNCTION:
case OFPACT_SAMPLE:
case OFPACT_STACK_POP:
case OFPACT_STACK_PUSH:
+ case OFPACT_DEBUG_RECIRC:
/* The action set may only include actions and thus
* may not include any instructions */
* "Action Set" and "Action List" terms used in OpenFlow 1.1+.)
*
* In general this involves appending the last instance of each action that is
- * adimissible in the action set in the order described in the OpenFlow
+ * admissible in the action set in the order described in the OpenFlow
* specification.
*
* Exceptions:
case OFPACT_MULTIPATH:
case OFPACT_NOTE:
case OFPACT_EXIT:
+ case OFPACT_UNROLL_XLATE:
case OFPACT_SAMPLE:
+ case OFPACT_DEBUG_RECIRC:
default:
return OVSINST_OFPIT11_APPLY_ACTIONS;
}
return ofpacts_pull_openflow_actions__(openflow, instructions_len,
version,
(1u << N_OVS_INSTRUCTIONS) - 1,
- ofpacts);
+ ofpacts, 0);
}
ofpbuf_clear(ofpacts);
ofpact_pad(ofpacts);
start = ofpacts->size;
- on = ofpact_put(ofpacts, OFPACT_WRITE_ACTIONS,
- offsetof(struct ofpact_nest, actions));
+ ofpact_put(ofpacts, OFPACT_WRITE_ACTIONS,
+ offsetof(struct ofpact_nest, actions));
get_actions_from_instruction(insts[OVSINST_OFPIT11_WRITE_ACTIONS],
&actions, &actions_len);
error = ofpacts_decode_for_action_set(actions, actions_len,
}
error = ofpacts_verify(ofpacts->data, ofpacts->size,
- (1u << N_OVS_INSTRUCTIONS) - 1);
+ (1u << N_OVS_INSTRUCTIONS) - 1, 0);
exit:
if (error) {
ofpbuf_clear(ofpacts);
case OFPP_FLOOD:
case OFPP_ALL:
case OFPP_CONTROLLER:
- case OFPP_NONE:
case OFPP_LOCAL:
return 0;
+ case OFPP_NONE:
+ return OFPERR_OFPBAC_BAD_OUT_PORT;
+
default:
if (ofp_to_u16(port) < ofp_to_u16(max_ports)) {
return 0;
case OFPACT_GROUP:
return 0;
+ case OFPACT_UNROLL_XLATE:
+ /* UNROLL is an internal action that should never be seen via
+ * OpenFlow. */
+ return OFPERR_OFPBAC_BAD_TYPE;
+
+ case OFPACT_DEBUG_RECIRC:
+ return 0;
+
default:
OVS_NOT_REACHED();
}
: 0);
}
+static enum ofperr
+ofpacts_verify_nested(const struct ofpact *a, enum ofpact_type outer_action)
+{
+ if (outer_action != OFPACT_WRITE_ACTIONS) {
+ VLOG_WARN("\"%s\" action doesn't support nested action \"%s\"",
+ ofpact_name(outer_action), ofpact_name(a->type));
+ return OFPERR_OFPBAC_BAD_ARGUMENT;
+ }
+
+ return 0;
+}
+
/* Verifies that the 'ofpacts_len' bytes of actions in 'ofpacts' are in the
* appropriate order as defined by the OpenFlow spec and as required by Open
* vSwitch.
*
* 'allowed_ovsinsts' is a bitmap of OVSINST_* values, in which 1-bits indicate
- * instructions that are allowed within 'ofpacts[]'. */
+ * instructions that are allowed within 'ofpacts[]'.
+ *
+ * If 'outer_action' is not zero, it specifies that the actions are nested
+ * within another action of type 'outer_action'. */
static enum ofperr
ofpacts_verify(const struct ofpact ofpacts[], size_t ofpacts_len,
- uint32_t allowed_ovsinsts)
+ uint32_t allowed_ovsinsts, enum ofpact_type outer_action)
{
const struct ofpact *a;
enum ovs_instruction_type inst;
if (a->type == OFPACT_CONJUNCTION) {
OFPACT_FOR_EACH (a, ofpacts, ofpacts_len) {
- if (a->type != OFPACT_CONJUNCTION) {
- VLOG_WARN("when %s action is present, it must be the only "
- "kind of action used", ofpact_name(a->type));
+ if (a->type != OFPACT_CONJUNCTION && a->type != OFPACT_NOTE) {
+ VLOG_WARN("\"conjunction\" actions may be used along with "
+ "\"note\" but not any other kind of action "
+ "(such as the \"%s\" action used here)",
+ ofpact_name(a->type));
return OFPERR_NXBAC_BAD_CONJUNCTION;
}
}
return 0;
}
+ if (outer_action) {
+ enum ofperr error = ofpacts_verify_nested(a, outer_action);
+
+ if (error) {
+ return error;
+ }
+ }
+
next = ovs_instruction_type_from_ofpact_type(a->type);
if (a > ofpacts
&& (inst == OVSINST_OFPIT11_APPLY_ACTIONS
case OFPACT_MULTIPATH:
case OFPACT_NOTE:
case OFPACT_EXIT:
+ case OFPACT_UNROLL_XLATE:
case OFPACT_PUSH_MPLS:
case OFPACT_POP_MPLS:
case OFPACT_SAMPLE:
case OFPACT_GOTO_TABLE:
case OFPACT_METER:
case OFPACT_GROUP:
+ case OFPACT_DEBUG_RECIRC:
default:
return false;
}
/* Parses 'str' as a series of instructions, and appends them to 'ofpacts'.
*
* Returns NULL if successful, otherwise a malloc()'d string describing the
- * error. The caller is responsible for freeing the returned string. */
+ * error. The caller is responsible for freeing the returned string.
+ *
+ * If 'outer_action' is specified, indicates that the actions being parsed
+ * are nested within another action of the type specified in 'outer_action'. */
static char * OVS_WARN_UNUSED_RESULT
ofpacts_parse__(char *str, struct ofpbuf *ofpacts,
enum ofputil_protocol *usable_protocols,
- bool allow_instructions)
+ bool allow_instructions, enum ofpact_type outer_action)
{
int prev_inst = -1;
enum ofperr retval;
retval = ofpacts_verify(ofpacts->data, ofpacts->size,
(allow_instructions
? (1u << N_OVS_INSTRUCTIONS) - 1
- : 1u << OVSINST_OFPIT11_APPLY_ACTIONS));
+ : 1u << OVSINST_OFPIT11_APPLY_ACTIONS),
+ outer_action);
if (retval) {
return xstrdup("Incorrect instruction ordering");
}
static char * OVS_WARN_UNUSED_RESULT
ofpacts_parse(char *str, struct ofpbuf *ofpacts,
- enum ofputil_protocol *usable_protocols, bool allow_instructions)
+ enum ofputil_protocol *usable_protocols, bool allow_instructions,
+ enum ofpact_type outer_action)
{
uint32_t orig_size = ofpacts->size;
char *error = ofpacts_parse__(str, ofpacts, usable_protocols,
- allow_instructions);
+ allow_instructions, outer_action);
if (error) {
ofpacts->size = orig_size;
}
static char * OVS_WARN_UNUSED_RESULT
ofpacts_parse_copy(const char *s_, struct ofpbuf *ofpacts,
enum ofputil_protocol *usable_protocols,
- bool allow_instructions)
+ bool allow_instructions, enum ofpact_type outer_action)
{
char *error, *s;
*usable_protocols = OFPUTIL_P_ANY;
s = xstrdup(s_);
- error = ofpacts_parse(s, ofpacts, usable_protocols, allow_instructions);
+ error = ofpacts_parse(s, ofpacts, usable_protocols, allow_instructions,
+ outer_action);
free(s);
return error;
}
/* Parses 's' as a set of OpenFlow actions and appends the actions to
- * 'ofpacts'.
+ * 'ofpacts'. 'outer_action', if nonzero, specifies that 's' contains actions
+ * that are nested within the action of type 'outer_action'.
*
* Returns NULL if successful, otherwise a malloc()'d string describing the
* error. The caller is responsible for freeing the returned string. */
ofpacts_parse_actions(const char *s, struct ofpbuf *ofpacts,
enum ofputil_protocol *usable_protocols)
{
- return ofpacts_parse_copy(s, ofpacts, usable_protocols, false);
+ return ofpacts_parse_copy(s, ofpacts, usable_protocols, false, 0);
}
/* Parses 's' as a set of OpenFlow instructions and appends the instructions to
ofpacts_parse_instructions(const char *s, struct ofpbuf *ofpacts,
enum ofputil_protocol *usable_protocols)
{
- return ofpacts_parse_copy(s, ofpacts, usable_protocols, true);
+ return ofpacts_parse_copy(s, ofpacts, usable_protocols, true, 0);
}
const char *