/*
- * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
memcpy(note->data, nan->note, length);
}
+static enum ofperr
+dec_ttl_from_openflow(struct ofpbuf *out)
+{
+ 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->n_controllers = 1;
+ ofpbuf_put(out, &id, sizeof id);
+ ids = out->l2;
+ ofpact_update_len(out, &ids->ofpact);
+ return error;
+}
+
+static enum ofperr
+dec_ttl_cnt_ids_from_openflow(const struct nx_action_cnt_ids *nac_ids,
+ struct ofpbuf *out)
+{
+ struct ofpact_cnt_ids *ids;
+ size_t ids_size;
+ int i;
+
+ ids = ofpact_put_DEC_TTL(out);
+ ids->ofpact.compat = OFPUTIL_NXAST_DEC_TTL_CNT_IDS;
+ ids->n_controllers = ntohs(nac_ids->n_controllers);
+ ids_size = ntohs(nac_ids->len) - sizeof *nac_ids;
+
+ if (!is_all_zeros(nac_ids->zeros, sizeof nac_ids->zeros)) {
+ return OFPERR_NXBRC_MUST_BE_ZERO;
+ }
+
+ if (ids_size < ids->n_controllers * sizeof(ovs_be16)) {
+ VLOG_WARN_RL(&rl, "Nicira action dec_ttl_cnt_ids only has %zu bytes "
+ "allocated for controller ids. %zu bytes are required for "
+ "%"PRIu16" controllers.", ids_size,
+ ids->n_controllers * sizeof(ovs_be16), ids->n_controllers);
+ return OFPERR_OFPBAC_BAD_LEN;
+ }
+
+ for (i = 0; i < ids->n_controllers; i++) {
+ uint16_t id = ntohs(((ovs_be16 *)(nac_ids + 1))[i]);
+ ofpbuf_put(out, &id, sizeof id);
+ }
+
+ ids = out->l2;
+ ofpact_update_len(out, &ids->ofpact);
+
+ return 0;
+}
+
static enum ofperr
decode_nxast_action(const union ofp_action *a, enum ofputil_action_code *code)
{
switch (code) {
case OFPUTIL_ACTION_INVALID:
#define OFPAT10_ACTION(ENUM, STRUCT, NAME) case OFPUTIL_##ENUM:
-#define OFPAT11_ACTION(ENUM, STRUCT, NAME) case OFPUTIL_##ENUM:
+#define OFPAT11_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME) case OFPUTIL_##ENUM:
#include "ofp-util.def"
NOT_REACHED();
ofpact_put_MULTIPATH(out));
break;
- case OFPUTIL_NXAST_AUTOPATH:
+ case OFPUTIL_NXAST_AUTOPATH__DEPRECATED:
error = autopath_from_openflow((const struct nx_action_autopath *) a,
ofpact_put_AUTOPATH(out));
break;
break;
case OFPUTIL_NXAST_DEC_TTL:
- ofpact_put_DEC_TTL(out);
+ error = dec_ttl_from_openflow(out);
+ break;
+
+ case OFPUTIL_NXAST_DEC_TTL_CNT_IDS:
+ error = dec_ttl_cnt_ids_from_openflow(
+ (const struct nx_action_cnt_ids *) a, out);
break;
case OFPUTIL_NXAST_FIN_TIMEOUT:
switch (code) {
case OFPUTIL_ACTION_INVALID:
-#define OFPAT11_ACTION(ENUM, STRUCT, NAME) case OFPUTIL_##ENUM:
+#define OFPAT11_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME) case OFPUTIL_##ENUM:
#include "ofp-util.def"
NOT_REACHED();
((LEFT) -= ntohs((ITER)->header.len) / sizeof(union ofp_action), \
(ITER) = action_next(ITER)))
+static void
+log_bad_action(const union ofp_action *actions, size_t n_actions, size_t ofs,
+ enum ofperr error)
+{
+ if (!VLOG_DROP_WARN(&rl)) {
+ struct ds s;
+
+ ds_init(&s);
+ ds_put_hex_dump(&s, actions, n_actions * sizeof *actions, 0, false);
+ VLOG_WARN("bad action at offset %#zx (%s):\n%s",
+ ofs * sizeof *actions, ofperr_get_name(error), ds_cstr(&s));
+ ds_destroy(&s);
+ }
+}
+
static enum ofperr
-ofpacts_from_openflow10(const union ofp_action *in, size_t n_in,
- struct ofpbuf *out)
+ofpacts_from_openflow(const union ofp_action *in, size_t n_in,
+ struct ofpbuf *out,
+ enum ofperr (*ofpact_from_openflow)(
+ const union ofp_action *a, struct ofpbuf *out))
{
const union ofp_action *a;
size_t left;
ACTION_FOR_EACH (a, left, in, n_in) {
- enum ofperr error = ofpact_from_openflow10(a, out);
+ enum ofperr error = ofpact_from_openflow(a, out);
if (error) {
- VLOG_WARN_RL(&rl, "bad action at offset %td (%s)",
- (a - in) * sizeof *a, ofperr_get_name(error));
+ log_bad_action(in, n_in, a - in, error);
return error;
}
}
if (left) {
- VLOG_WARN_RL(&rl, "bad action format at offset %zu",
- (n_in - left) * sizeof *a);
- return OFPERR_OFPBAC_BAD_LEN;
+ enum ofperr error = OFPERR_OFPBAC_BAD_LEN;
+ log_bad_action(in, n_in, n_in - left, error);
+ return error;
}
ofpact_pad(out);
return 0;
}
+static enum ofperr
+ofpacts_from_openflow10(const union ofp_action *in, size_t n_in,
+ struct ofpbuf *out)
+{
+ return ofpacts_from_openflow(in, n_in, out, ofpact_from_openflow10);
+}
+
static enum ofperr
ofpacts_pull_actions(struct ofpbuf *openflow, unsigned int actions_len,
struct ofpbuf *ofpacts,
* in 'ofpacts' by the converted ofpacts; on failure, clears 'ofpacts'.
* Returns 0 if successful, otherwise an OpenFlow error.
*
- * This function does not check that the actions are valid in a given context.
- * The caller should do so, with ofpacts_check(). */
+ * 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_openflow10(struct ofpbuf *openflow, unsigned int actions_len,
struct ofpbuf *ofpacts)
decode_openflow11_action(const union ofp_action *a,
enum ofputil_action_code *code)
{
+ uint16_t len;
+
switch (a->type) {
case CONSTANT_HTONS(OFPAT11_EXPERIMENTER):
return decode_nxast_action(a, code);
-#define OFPAT11_ACTION(ENUM, STRUCT, NAME) \
- case CONSTANT_HTONS(ENUM): \
- if (a->header.len == htons(sizeof(struct STRUCT))) { \
- *code = OFPUTIL_##ENUM; \
- return 0; \
- } else { \
- return OFPERR_OFPBAC_BAD_LEN; \
- } \
- break;
+#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:
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);
+
#define NXAST_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME) case OFPUTIL_##ENUM:
#include "ofp-util.def"
return ofpact_from_nxast(a, code, out);
ofpacts_from_openflow11(const union ofp_action *in, size_t n_in,
struct ofpbuf *out)
{
- const union ofp_action *a;
- size_t left;
-
- ACTION_FOR_EACH (a, left, in, n_in) {
- enum ofperr error = ofpact_from_openflow11(a, out);
- if (error) {
- VLOG_WARN_RL(&rl, "bad action at offset %td (%s)",
- (a - in) * sizeof *a, ofperr_get_name(error));
- return error;
- }
- }
- if (left) {
- VLOG_WARN_RL(&rl, "bad action format at offset %zu",
- (n_in - left) * sizeof *a);
- return OFPERR_OFPBAC_BAD_LEN;
- }
-
- return 0;
+ return ofpacts_from_openflow(in, n_in, out, ofpact_from_openflow11);
}
\f
/* OpenFlow 1.1 instructions. */
-#define OVS_INSTRUCTIONS \
- DEFINE_INST(OFPIT11_GOTO_TABLE, \
- ofp11_instruction_goto_table, false, \
- "goto_table") \
- \
- DEFINE_INST(OFPIT11_WRITE_METADATA, \
- ofp11_instruction_write_metadata, false, \
- "write_metadata") \
- \
- DEFINE_INST(OFPIT11_WRITE_ACTIONS, \
- ofp11_instruction_actions, true, \
- "write_actions") \
- \
- DEFINE_INST(OFPIT11_APPLY_ACTIONS, \
- ofp11_instruction_actions, true, \
- "apply_actions") \
- \
- DEFINE_INST(OFPIT11_CLEAR_ACTIONS, \
- ofp11_instruction, false, \
- "clear_actions")
-
-enum ovs_instruction_type {
-#define DEFINE_INST(ENUM, STRUCT, EXTENSIBLE, NAME) OVSINST_##ENUM,
- OVS_INSTRUCTIONS
-#undef DEFINE_INST
-};
-
-enum {
-#define DEFINE_INST(ENUM, STRUCT, EXTENSIBLE, NAME) + 1
- N_OVS_INSTRUCTIONS = OVS_INSTRUCTIONS
-#undef DEFINE_INST
-};
-
#define DEFINE_INST(ENUM, STRUCT, EXTENSIBLE, NAME) \
+ static inline const struct STRUCT * \
+ instruction_get_##ENUM(const struct ofp11_instruction *inst)\
+ { \
+ assert(inst->type == htons(ENUM)); \
+ return (struct STRUCT *)inst; \
+ } \
+ \
static inline void \
instruction_init_##ENUM(struct STRUCT *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 *
+ofpact_instruction_name_from_type(enum ovs_instruction_type type)
+{
+ return inst_info[type].name;
+}
+
+int
+ofpact_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;
+}
+
static inline struct ofp11_instruction *
instruction_next(const struct ofp11_instruction *inst)
{
}
if (out[type]) {
- return OFPERR_NXBIC_DUP_TYPE;
+ return OFPERR_OFPIT_BAD_INSTRUCTION;
}
out[type] = inst;
}
* instructions, so you should call ofpacts_pull_openflow11_instructions()
* instead of this function.
*
- * This function does not check that the actions are valid in a given context.
- * The caller should do so, with ofpacts_check(). */
+ * 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)
{
- enum ofperr error;
-
- error = ofpacts_pull_actions(openflow, actions_len, ofpacts,
- ofpacts_from_openflow11);
- if (!error) {
- ofpact_pad(ofpacts);
- }
- return error;
+ return ofpacts_pull_actions(openflow, actions_len, ofpacts,
+ ofpacts_from_openflow11);
}
enum ofperr
}
}
- ofpact_pad(ofpacts);
-
if (insts[OVSINST_OFPIT11_GOTO_TABLE] ||
insts[OVSINST_OFPIT11_WRITE_METADATA] ||
insts[OVSINST_OFPIT11_WRITE_ACTIONS] ||
nac->reason = oc->reason;
}
+static void
+ofpact_dec_ttl_to_nxast(const struct ofpact_cnt_ids *oc_ids,
+ struct ofpbuf *out)
+{
+ if (oc_ids->ofpact.compat == OFPUTIL_NXAST_DEC_TTL) {
+ ofputil_put_NXAST_DEC_TTL(out);
+ } else {
+ struct nx_action_cnt_ids *nac_ids =
+ ofputil_put_NXAST_DEC_TTL_CNT_IDS(out);
+ int ids_len = ROUND_UP(2 * oc_ids->n_controllers, OFP_ACTION_ALIGN);
+ ovs_be16 *ids;
+ size_t i;
+
+ nac_ids->len = htons(ntohs(nac_ids->len) + ids_len);
+ nac_ids->n_controllers = htons(oc_ids->n_controllers);
+
+ ids = ofpbuf_put_zeros(out, ids_len);
+ for (i = 0; i < oc_ids->n_controllers; i++) {
+ ids[i] = htons(oc_ids->cnt_ids[i]);
+ }
+ }
+}
+
static void
ofpact_fin_timeout_to_nxast(const struct ofpact_fin_timeout *fin_timeout,
struct ofpbuf *out)
break;
case OFPACT_DEC_TTL:
- ofputil_put_NXAST_DEC_TTL(out);
+ ofpact_dec_ttl_to_nxast(ofpact_get_DEC_TTL(a), out);
break;
case OFPACT_SET_TUNNEL:
/* Converts the ofpacts in 'ofpacts' (terminated by OFPACT_END) into OpenFlow
* 1.1 actions in 'openflow', appending the actions to any existing data in
* 'openflow'. */
-void
+size_t
ofpacts_put_openflow11_actions(const struct ofpact ofpacts[],
size_t ofpacts_len, struct ofpbuf *openflow)
{
const struct ofpact *a;
+ size_t start_size = openflow->size;
OFPACT_FOR_EACH (a, ofpacts, ofpacts_len) {
ofpact_to_openflow11(a, openflow);
}
+
+ return openflow->size - start_size;
}
void
}
}
+static void
+print_dec_ttl(const struct ofpact_cnt_ids *ids,
+ struct ds *s)
+{
+ size_t i;
+
+ ds_put_cstr(s, "dec_ttl");
+ if (ids->ofpact.compat == OFPUTIL_NXAST_DEC_TTL_CNT_IDS) {
+ ds_put_cstr(s, "(");
+ for (i = 0; i < ids->n_controllers; i++) {
+ if (i) {
+ ds_put_cstr(s, ",");
+ }
+ ds_put_format(s, "%"PRIu16, ids->cnt_ids[i]);
+ }
+ ds_put_cstr(s, ")");
+ }
+}
+
static void
print_fin_timeout(const struct ofpact_fin_timeout *fin_timeout,
struct ds *s)
break;
case OFPACT_DEC_TTL:
- ds_put_cstr(s, "dec_ttl");
+ print_dec_ttl(ofpact_get_DEC_TTL(a), s);
break;
case OFPACT_SET_TUNNEL:
case OFPACT_RESUBMIT:
resubmit = ofpact_get_RESUBMIT(a);
if (resubmit->in_port != OFPP_IN_PORT && resubmit->table_id == 255) {
- ds_put_format(s, "resubmit:%"PRIu16, resubmit->in_port);
+ ds_put_cstr(s, "resubmit:");
+ ofputil_format_port(resubmit->in_port, s);
} else {
ds_put_format(s, "resubmit(");
if (resubmit->in_port != OFPP_IN_PORT) {
case OFPACT_AUTOPATH:
autopath = ofpact_get_AUTOPATH(a);
- ds_put_format(s, "autopath(%u,", autopath->port);
+ ds_put_cstr(s, "autopath(");
+ ofputil_format_port(autopath->port, s);
+ ds_put_char(s, ',');
mf_format_subfield(&autopath->dst, s);
ds_put_char(s, ')');
break;
ofpbuf_put_zeros(ofpacts, OFPACT_ALIGNTO - rem);
}
}
+
+void
+ofpact_set_field_init(struct ofpact_reg_load *load, const struct mf_field *mf,
+ const void *src)
+{
+ load->ofpact.compat = OFPUTIL_OFPAT12_SET_FIELD;
+ load->dst.field = mf;
+ load->dst.ofs = 0;
+ load->dst.n_bits = mf->n_bits;
+ bitwise_copy(src, mf->n_bytes, load->dst.ofs,
+ &load->subvalue, sizeof load->subvalue, 0, mf->n_bits);
+}