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
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:
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. */
return ofpacts_pull_openflow_actions__(openflow, instructions_len,
version,
(1u << N_OVS_INSTRUCTIONS) - 1,
- ofpacts);
+ ofpacts, 0);
}
ofpbuf_clear(ofpacts);
}
error = ofpacts_verify(ofpacts->data, ofpacts->size,
- (1u << N_OVS_INSTRUCTIONS) - 1);
+ (1u << N_OVS_INSTRUCTIONS) - 1, 0);
exit:
if (error) {
ofpbuf_clear(ofpacts);
: 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;
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
/* 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 *