OpenFlow actions have always been somewhat awkward to handle.
Moreover, over time we've started creating actions that require more
complicated parsing. When we maintain those actions internally in
their wire format, we end up parsing them multiple times, whenever
we have to look at the set of actions.
When we add support for OpenFlow 1.1 or later protocols, the situation
will get worse, because these newer protocols support many of the same
actions but with different representations. It becomes unrealistic to
handle each protocol in its wire format.
This commit adopts a new strategy, by converting OpenFlow actions into
an internal form from the wire format when they are read, and converting
them back to the wire format when flows are dumped. I believe that this
will be more maintainable over time.
Thanks to Simon Horman and Pravin Shelar for reviews.
Signed-off-by: Ben Pfaff <blp@nicira.com>
gateway.
+Action Reproduction
+===================
+
+It seems likely that many controllers, at least at startup, use the
+OpenFlow "flow statistics" request to obtain existing flows, then
+compare the flows' actions against the actions that they expect to
+find. Before version 1.8.0, Open vSwitch always returned exact,
+byte-for-byte copies of the actions that had been added to the flow
+table. The current version of Open vSwitch does not always do this in
+some exceptional cases. This section lists the exceptions that
+controller authors must keep in mind if they compare actual actions
+against desired actions in a bytewise fashion:
+
+ - Open vSwitch zeros padding bytes in action structures,
+ regardless of their values when the flows were added.
+
+Please report other discrepancies, if you notice any, so that we can
+fix or document them.
+
+
Suggestions
===========
post-v1.7.0
------------------------
- New FAQ. Please send updates and additions!
+ - Authors of controllers, please read the new section titled "Action
+ Reproduction" in DESIGN, which describes an Open vSwitch change in
+ behavior in corner cases that may affect some controllers.
- ovs-l3ping:
- A new test utility that can create L3 tunnel between two Open
vSwitches and detect connectivity issues.
lib/nx-match.h \
lib/odp-util.c \
lib/odp-util.h \
+ lib/ofp-actions.c \
+ lib/ofp-actions.h \
lib/ofp-errors.c \
lib/ofp-errors.h \
lib/ofp-parse.c \
/*
- * Copyright (c) 2011 Nicira, Inc.
+ * Copyright (c) 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.
#include "flow.h"
#include "meta-flow.h"
#include "nx-match.h"
+#include "ofp-actions.h"
#include "ofp-errors.h"
#include "ofp-util.h"
#include "openflow/nicira-ext.h"
VLOG_DEFINE_THIS_MODULE(autopath);
-/* Loads 'ofp_port' into the appropriate register in accordance with the
- * autopath action. */
void
-autopath_execute(const struct nx_action_autopath *ap, struct flow *flow,
- uint16_t ofp_port)
-{
- struct mf_subfield dst;
-
- nxm_decode(&dst, ap->dst, ap->ofs_nbits);
- mf_set_subfield_value(&dst, ofp_port, flow);
-}
-
-void
-autopath_parse(struct nx_action_autopath *ap, const char *s_)
+autopath_parse(struct ofpact_autopath *ap, const char *s_)
{
char *s;
- char *id_str, *dst_s, *save_ptr;
- struct mf_subfield dst;
int id_int;
+ char *id_str, *dst, *save_ptr;
+
+ ofpact_init_AUTOPATH(ap);
s = xstrdup(s_);
save_ptr = NULL;
id_str = strtok_r(s, ", ", &save_ptr);
- dst_s = strtok_r(NULL, ", ", &save_ptr);
+ dst = strtok_r(NULL, ", ", &save_ptr);
- if (!dst_s) {
+ if (!dst) {
ovs_fatal(0, "%s: not enough arguments to autopath action", s_);
}
ovs_fatal(0, "%s: autopath id %d is not in valid range "
"1 to %"PRIu32, s_, id_int, UINT32_MAX);
}
+ ap->port = id_int;
- mf_parse_subfield(&dst, dst_s);
- if (dst.n_bits < 16) {
+ mf_parse_subfield(&ap->dst, dst);
+ if (ap->dst.n_bits < 16) {
ovs_fatal(0, "%s: %d-bit destination field has %u possible values, "
"less than required 65536",
- s_, dst.n_bits, 1u << dst.n_bits);
+ s_, ap->dst.n_bits, 1u << ap->dst.n_bits);
}
- ofputil_init_NXAST_AUTOPATH(ap);
- ap->id = htonl(id_int);
- ap->ofs_nbits = nxm_encode_ofs_nbits(dst.ofs, dst.n_bits);
- ap->dst = htonl(dst.field->nxm_header);
-
free(s);
}
enum ofperr
-autopath_check(const struct nx_action_autopath *ap, const struct flow *flow)
+autopath_from_openflow(const struct nx_action_autopath *nap,
+ struct ofpact_autopath *autopath)
{
- struct mf_subfield dst;
+ ofpact_init_AUTOPATH(autopath);
+ autopath->dst.field = mf_from_nxm_header(ntohl(nap->dst));
+ autopath->dst.ofs = nxm_decode_ofs(nap->ofs_nbits);
+ autopath->dst.n_bits = nxm_decode_n_bits(nap->ofs_nbits);
+ autopath->port = ntohl(nap->id);
- nxm_decode(&dst, ap->dst, ap->ofs_nbits);
- if (dst.n_bits < 16) {
+ if (autopath->dst.n_bits < 16) {
VLOG_WARN("at least 16 bit destination is required for autopath "
"action.");
return OFPERR_OFPBAC_BAD_ARGUMENT;
}
- return mf_check_dst(&dst, flow);
+ return autopath_check(autopath, NULL);
+}
+
+enum ofperr
+autopath_check(const struct ofpact_autopath *autopath, const struct flow *flow)
+{
+ return mf_check_dst(&autopath->dst, flow);
+}
+
+void
+autopath_to_nxast(const struct ofpact_autopath *autopath,
+ struct ofpbuf *openflow)
+{
+ struct nx_action_autopath *ap = ofputil_put_NXAST_AUTOPATH(openflow);
+
+ ap->ofs_nbits = nxm_encode_ofs_nbits(autopath->dst.ofs,
+ autopath->dst.n_bits);
+ ap->dst = htonl(autopath->dst.field->nxm_header);
+ ap->id = htonl(autopath->port);
}
/*
- * Copyright (c) 2011 Nicira, Inc.
+ * Copyright (c) 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.
struct flow;
struct nx_action_autopath;
+struct ofpact_autopath;
+struct ofpbuf;
/* NXAST_AUTOPATH helper functions.
*
* See include/openflow/nicira-ext.h for NXAST_AUTOPATH specification. */
-void autopath_execute(const struct nx_action_autopath *, struct flow *,
- uint16_t ofp_port);
-void autopath_parse(struct nx_action_autopath *, const char *);
-enum ofperr autopath_check(const struct nx_action_autopath *,
+void autopath_parse(struct ofpact_autopath *, const char *);
+
+enum ofperr autopath_from_openflow(const struct nx_action_autopath *,
+ struct ofpact_autopath *);
+enum ofperr autopath_check(const struct ofpact_autopath *,
const struct flow *);
+void autopath_to_nxast(const struct ofpact_autopath *,
+ struct ofpbuf *openflow);
#endif /* autopath.h */
#include "meta-flow.h"
#include "nx-match.h"
#include "ofpbuf.h"
+#include "ofp-actions.h"
#include "ofp-errors.h"
#include "ofp-util.h"
#include "openflow/nicira-ext.h"
VLOG_DEFINE_THIS_MODULE(bundle);
static uint16_t
-execute_ab(const struct nx_action_bundle *nab,
+execute_ab(const struct ofpact_bundle *bundle,
bool (*slave_enabled)(uint16_t ofp_port, void *aux), void *aux)
{
size_t i;
- for (i = 0; i < ntohs(nab->n_slaves); i++) {
- uint16_t slave = bundle_get_slave(nab, i);
-
+ for (i = 0; i < bundle->n_slaves; i++) {
+ uint16_t slave = bundle->slaves[i];
if (slave_enabled(slave, aux)) {
return slave;
}
}
static uint16_t
-execute_hrw(const struct nx_action_bundle *nab, const struct flow *flow,
+execute_hrw(const struct ofpact_bundle *bundle, const struct flow *flow,
bool (*slave_enabled)(uint16_t ofp_port, void *aux), void *aux)
{
uint32_t flow_hash, best_hash;
int best, i;
- flow_hash = flow_hash_fields(flow, ntohs(nab->fields), ntohs(nab->basis));
+ flow_hash = flow_hash_fields(flow, bundle->fields, bundle->basis);
best = -1;
best_hash = 0;
- for (i = 0; i < ntohs(nab->n_slaves); i++) {
- if (slave_enabled(bundle_get_slave(nab, i), aux)) {
+ for (i = 0; i < bundle->n_slaves; i++) {
+ if (slave_enabled(bundle->slaves[i], aux)) {
uint32_t hash = hash_2words(i, flow_hash);
if (best < 0 || hash > best_hash) {
}
}
- return best >= 0 ? bundle_get_slave(nab, best) : OFPP_NONE;
+ return best >= 0 ? bundle->slaves[best] : OFPP_NONE;
}
-/* Executes 'nab' on 'flow'. Uses 'slave_enabled' to determine if the slave
+/* Executes 'bundle' on 'flow'. Uses 'slave_enabled' to determine if the slave
* designated by 'ofp_port' is up. Returns the chosen slave, or OFPP_NONE if
* none of the slaves are acceptable. */
uint16_t
-bundle_execute(const struct nx_action_bundle *nab, const struct flow *flow,
+bundle_execute(const struct ofpact_bundle *bundle, const struct flow *flow,
bool (*slave_enabled)(uint16_t ofp_port, void *aux), void *aux)
{
- switch (ntohs(nab->algorithm)) {
- case NX_BD_ALG_HRW: return execute_hrw(nab, flow, slave_enabled, aux);
- case NX_BD_ALG_ACTIVE_BACKUP: return execute_ab(nab, slave_enabled, aux);
- default: NOT_REACHED();
- }
-}
+ switch (bundle->algorithm) {
+ case NX_BD_ALG_HRW:
+ return execute_hrw(bundle, flow, slave_enabled, aux);
-void
-bundle_execute_load(const struct nx_action_bundle *nab, struct flow *flow,
- bool (*slave_enabled)(uint16_t ofp_port, void *aux),
- void *aux)
-{
- struct mf_subfield dst;
+ case NX_BD_ALG_ACTIVE_BACKUP:
+ return execute_ab(bundle, slave_enabled, aux);
- nxm_decode(&dst, nab->dst, nab->ofs_nbits);
- mf_set_subfield_value(&dst, bundle_execute(nab, flow, slave_enabled, aux),
- flow);
+ default:
+ NOT_REACHED();
+ }
}
/* Checks that 'nab' specifies a bundle action which is supported by this
* ofputil_check_output_port(). Returns 0 if 'nab' is supported, otherwise an
* OFPERR_* error code. */
enum ofperr
-bundle_check(const struct nx_action_bundle *nab, int max_ports,
- const struct flow *flow)
+bundle_from_openflow(const struct nx_action_bundle *nab,
+ struct ofpbuf *ofpacts)
{
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
- uint16_t n_slaves, fields, algorithm, subtype;
+ struct ofpact_bundle *bundle;
+ uint16_t subtype;
uint32_t slave_type;
size_t slaves_size, i;
enum ofperr error;
+ bundle = ofpact_put_BUNDLE(ofpacts);
+
subtype = ntohs(nab->subtype);
- n_slaves = ntohs(nab->n_slaves);
- fields = ntohs(nab->fields);
- algorithm = ntohs(nab->algorithm);
+ bundle->n_slaves = ntohs(nab->n_slaves);
+ bundle->basis = ntohs(nab->basis);
+ bundle->fields = ntohs(nab->fields);
+ bundle->algorithm = ntohs(nab->algorithm);
slave_type = ntohl(nab->slave_type);
slaves_size = ntohs(nab->len) - sizeof *nab;
error = OFPERR_OFPBAC_BAD_ARGUMENT;
- if (!flow_hash_fields_valid(fields)) {
- VLOG_WARN_RL(&rl, "unsupported fields %"PRIu16, fields);
- } else if (n_slaves > BUNDLE_MAX_SLAVES) {
+ if (!flow_hash_fields_valid(bundle->fields)) {
+ VLOG_WARN_RL(&rl, "unsupported fields %d", (int) bundle->fields);
+ } else if (bundle->n_slaves > BUNDLE_MAX_SLAVES) {
VLOG_WARN_RL(&rl, "too may slaves");
- } else if (algorithm != NX_BD_ALG_HRW
- && algorithm != NX_BD_ALG_ACTIVE_BACKUP) {
- VLOG_WARN_RL(&rl, "unsupported algorithm %"PRIu16, algorithm);
+ } else if (bundle->algorithm != NX_BD_ALG_HRW
+ && bundle->algorithm != NX_BD_ALG_ACTIVE_BACKUP) {
+ VLOG_WARN_RL(&rl, "unsupported algorithm %d", (int) bundle->algorithm);
} else if (slave_type != NXM_OF_IN_PORT) {
VLOG_WARN_RL(&rl, "unsupported slave type %"PRIu16, slave_type);
} else {
error = 0;
}
- for (i = 0; i < sizeof(nab->zero); i++) {
- if (nab->zero[i]) {
- VLOG_WARN_RL(&rl, "reserved field is nonzero");
- error = OFPERR_OFPBAC_BAD_ARGUMENT;
- }
+ if (!is_all_zeros(nab->zero, sizeof nab->zero)) {
+ VLOG_WARN_RL(&rl, "reserved field is nonzero");
+ error = OFPERR_OFPBAC_BAD_ARGUMENT;
}
if (subtype == NXAST_BUNDLE && (nab->ofs_nbits || nab->dst)) {
}
if (subtype == NXAST_BUNDLE_LOAD) {
- struct mf_subfield dst;
+ bundle->dst.field = mf_from_nxm_header(ntohl(nab->dst));
+ bundle->dst.ofs = nxm_decode_ofs(nab->ofs_nbits);
+ bundle->dst.n_bits = nxm_decode_n_bits(nab->ofs_nbits);
- nxm_decode(&dst, nab->dst, nab->ofs_nbits);
- if (dst.n_bits < 16) {
+ if (bundle->dst.n_bits < 16) {
VLOG_WARN_RL(&rl, "bundle_load action requires at least 16 bit "
"destination.");
error = OFPERR_OFPBAC_BAD_ARGUMENT;
- } else if (!error) {
- error = mf_check_dst(&dst, flow);
}
}
- if (slaves_size < n_slaves * sizeof(ovs_be16)) {
+ if (slaves_size < bundle->n_slaves * sizeof(ovs_be16)) {
VLOG_WARN_RL(&rl, "Nicira action %"PRIu16" only has %zu bytes "
"allocated for slaves. %zu bytes are required for "
"%"PRIu16" slaves.", subtype, slaves_size,
- n_slaves * sizeof(ovs_be16), n_slaves);
+ bundle->n_slaves * sizeof(ovs_be16), bundle->n_slaves);
error = OFPERR_OFPBAC_BAD_LEN;
}
- for (i = 0; i < n_slaves; i++) {
- uint16_t ofp_port = bundle_get_slave(nab, i);
- enum ofperr ofputil_error;
+ for (i = 0; i < bundle->n_slaves; i++) {
+ uint16_t ofp_port = ntohs(((ovs_be16 *)(nab + 1))[i]);
+ ofpbuf_put(ofpacts, &ofp_port, sizeof ofp_port);
+ }
+
+ bundle = ofpacts->l2;
+ ofpact_update_len(ofpacts, &bundle->ofpact);
+
+ if (!error) {
+ error = bundle_check(bundle, OFPP_MAX, NULL);
+ }
+ return error;
+}
+
+enum ofperr
+bundle_check(const struct ofpact_bundle *bundle, int max_ports,
+ const struct flow *flow)
+{
+ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
+ size_t i;
+
+ if (bundle->dst.field) {
+ enum ofperr error = mf_check_dst(&bundle->dst, flow);
+ if (error) {
+ return error;
+ }
+ }
+
+ for (i = 0; i < bundle->n_slaves; i++) {
+ uint16_t ofp_port = bundle->slaves[i];
+ enum ofperr error;
- ofputil_error = ofputil_check_output_port(ofp_port, max_ports);
- if (ofputil_error) {
+ error = ofputil_check_output_port(ofp_port, max_ports);
+ if (error) {
VLOG_WARN_RL(&rl, "invalid slave %"PRIu16, ofp_port);
- error = ofputil_error;
+ return error;
}
/* Controller slaves are unsupported due to the lack of a max_len
* seem to be a real-world use-case for supporting it. */
if (ofp_port == OFPP_CONTROLLER) {
VLOG_WARN_RL(&rl, "unsupported controller slave");
- error = OFPERR_OFPBAC_BAD_OUT_PORT;
+ return OFPERR_OFPBAC_BAD_OUT_PORT;
}
}
- return error;
+ return 0;
+}
+
+void
+bundle_to_nxast(const struct ofpact_bundle *bundle, struct ofpbuf *openflow)
+{
+ int slaves_len = ROUND_UP(bundle->n_slaves, OFP_ACTION_ALIGN);
+ struct nx_action_bundle *nab;
+ ovs_be16 *slaves;
+ size_t i;
+
+ nab = (bundle->dst.field
+ ? ofputil_put_NXAST_BUNDLE_LOAD(openflow)
+ : ofputil_put_NXAST_BUNDLE(openflow));
+ nab->len = htons(ntohs(nab->len) + slaves_len);
+ nab->algorithm = htons(bundle->algorithm);
+ nab->fields = htons(bundle->fields);
+ nab->basis = htons(bundle->basis);
+ nab->slave_type = htonl(NXM_OF_IN_PORT);
+ nab->n_slaves = htons(bundle->n_slaves);
+ if (bundle->dst.field) {
+ nab->ofs_nbits = nxm_encode_ofs_nbits(bundle->dst.ofs,
+ bundle->dst.n_bits);
+ nab->dst = htonl(bundle->dst.field->nxm_header);
+ }
+
+ slaves = ofpbuf_put_zeros(openflow, slaves_len);
+ for (i = 0; i < bundle->n_slaves; i++) {
+ slaves[i] = htons(bundle->slaves[i]);
+ }
}
/* Helper for bundle_parse and bundle_parse_load. */
static void
-bundle_parse__(struct ofpbuf *b, const char *s, char **save_ptr,
+bundle_parse__(const char *s, char **save_ptr,
const char *fields, const char *basis, const char *algorithm,
- const char *slave_type, const char *dst_s,
- const char *slave_delim)
+ const char *slave_type, const char *dst,
+ const char *slave_delim, struct ofpbuf *ofpacts)
{
- enum ofputil_action_code code;
- struct nx_action_bundle *nab;
- uint16_t n_slaves;
+ struct ofpact_bundle *bundle;
if (!slave_delim) {
ovs_fatal(0, "%s: not enough arguments to bundle action", s);
s, slave_delim);
}
- code = dst_s ? OFPUTIL_NXAST_BUNDLE_LOAD : OFPUTIL_NXAST_BUNDLE;
- b->l2 = ofputil_put_action(code, b);
+ bundle = ofpact_put_BUNDLE(ofpacts);
- n_slaves = 0;
for (;;) {
- ovs_be16 slave_be;
+ uint16_t slave_port;
char *slave;
slave = strtok_r(NULL, ", [", save_ptr);
- if (!slave || n_slaves >= BUNDLE_MAX_SLAVES) {
+ if (!slave || bundle->n_slaves >= BUNDLE_MAX_SLAVES) {
break;
}
- slave_be = htons(atoi(slave));
- ofpbuf_put(b, &slave_be, sizeof slave_be);
+ slave_port = atoi(slave);
+ ofpbuf_put(ofpacts, &slave_port, sizeof slave_port);
- n_slaves++;
+ bundle = ofpacts->l2;
+ bundle->n_slaves++;
}
+ ofpact_update_len(ofpacts, &bundle->ofpact);
- /* Slaves array must be multiple of 8 bytes long. */
- if (b->size % 8) {
- ofpbuf_put_zeros(b, 8 - (b->size % 8));
- }
-
- nab = b->l2;
- nab->len = htons(b->size - ((char *) b->l2 - (char *) b->data));
- nab->n_slaves = htons(n_slaves);
- nab->basis = htons(atoi(basis));
+ bundle->basis = atoi(basis);
if (!strcasecmp(fields, "eth_src")) {
- nab->fields = htons(NX_HASH_FIELDS_ETH_SRC);
+ bundle->fields = NX_HASH_FIELDS_ETH_SRC;
} else if (!strcasecmp(fields, "symmetric_l4")) {
- nab->fields = htons(NX_HASH_FIELDS_SYMMETRIC_L4);
+ bundle->fields = NX_HASH_FIELDS_SYMMETRIC_L4;
} else {
ovs_fatal(0, "%s: unknown fields `%s'", s, fields);
}
if (!strcasecmp(algorithm, "active_backup")) {
- nab->algorithm = htons(NX_BD_ALG_ACTIVE_BACKUP);
+ bundle->algorithm = NX_BD_ALG_ACTIVE_BACKUP;
} else if (!strcasecmp(algorithm, "hrw")) {
- nab->algorithm = htons(NX_BD_ALG_HRW);
+ bundle->algorithm = NX_BD_ALG_HRW;
} else {
ovs_fatal(0, "%s: unknown algorithm `%s'", s, algorithm);
}
- if (!strcasecmp(slave_type, "ofport")) {
- nab->slave_type = htonl(NXM_OF_IN_PORT);
- } else {
+ if (strcasecmp(slave_type, "ofport")) {
ovs_fatal(0, "%s: unknown slave_type `%s'", s, slave_type);
}
- if (dst_s) {
- struct mf_subfield dst;
-
- mf_parse_subfield(&dst, dst_s);
- nab->dst = htonl(dst.field->nxm_header);
- nab->ofs_nbits = nxm_encode_ofs_nbits(dst.ofs, dst.n_bits);
+ if (dst) {
+ mf_parse_subfield(&bundle->dst, dst);
}
-
- b->l2 = NULL;
}
/* Converts a bundle action string contained in 's' to an nx_action_bundle and
* stores it in 'b'. Sets 'b''s l2 pointer to NULL. */
void
-bundle_parse(struct ofpbuf *b, const char *s)
+bundle_parse(const char *s, struct ofpbuf *ofpacts)
{
char *fields, *basis, *algorithm, *slave_type, *slave_delim;
char *tokstr, *save_ptr;
slave_type = strtok_r(NULL, ", ", &save_ptr);
slave_delim = strtok_r(NULL, ": ", &save_ptr);
- bundle_parse__(b, s, &save_ptr, fields, basis, algorithm, slave_type, NULL,
- slave_delim);
+ bundle_parse__(s, &save_ptr, fields, basis, algorithm, slave_type, NULL,
+ slave_delim, ofpacts);
free(tokstr);
}
/* Converts a bundle_load action string contained in 's' to an nx_action_bundle
* and stores it in 'b'. Sets 'b''s l2 pointer to NULL. */
void
-bundle_parse_load(struct ofpbuf *b, const char *s)
+bundle_parse_load(const char *s, struct ofpbuf *ofpacts)
{
char *fields, *basis, *algorithm, *slave_type, *dst, *slave_delim;
char *tokstr, *save_ptr;
dst = strtok_r(NULL, ", ", &save_ptr);
slave_delim = strtok_r(NULL, ": ", &save_ptr);
- bundle_parse__(b, s, &save_ptr, fields, basis, algorithm, slave_type, dst,
- slave_delim);
+ bundle_parse__(s, &save_ptr, fields, basis, algorithm, slave_type, dst,
+ slave_delim, ofpacts);
free(tokstr);
}
/* Appends a human-readable representation of 'nab' to 's'. */
void
-bundle_format(const struct nx_action_bundle *nab, struct ds *s)
+bundle_format(const struct ofpact_bundle *bundle, struct ds *s)
{
- const char *action, *fields, *algorithm, *slave_type;
+ const char *action, *fields, *algorithm;
size_t i;
- fields = flow_hash_fields_to_str(ntohs(nab->fields));
+ fields = flow_hash_fields_to_str(bundle->fields);
- switch (ntohs(nab->algorithm)) {
+ switch (bundle->algorithm) {
case NX_BD_ALG_HRW:
algorithm = "hrw";
break;
algorithm = "<unknown>";
}
- switch (ntohl(nab->slave_type)) {
- case NXM_OF_IN_PORT:
- slave_type = "ofport";
- break;
- default:
- slave_type = "<unknown>";
- }
-
- switch (ntohs(nab->subtype)) {
- case NXAST_BUNDLE:
- action = "bundle";
- break;
- case NXAST_BUNDLE_LOAD:
- action = "bundle_load";
- break;
- default:
- NOT_REACHED();
- }
+ action = bundle->dst.field ? "bundle_load" : "bundle";
ds_put_format(s, "%s(%s,%"PRIu16",%s,%s,", action, fields,
- ntohs(nab->basis), algorithm, slave_type);
-
- if (nab->subtype == htons(NXAST_BUNDLE_LOAD)) {
- struct mf_subfield dst;
+ bundle->basis, algorithm, "ofport");
- nxm_decode(&dst, nab->dst, nab->ofs_nbits);
- mf_format_subfield(&dst, s);
+ if (bundle->dst.field) {
+ mf_format_subfield(&bundle->dst, s);
ds_put_cstr(s, ",");
}
ds_put_cstr(s, "slaves:");
- for (i = 0; i < ntohs(nab->n_slaves); i++) {
+ for (i = 0; i < bundle->n_slaves; i++) {
if (i) {
ds_put_cstr(s, ",");
}
- ds_put_format(s, "%"PRIu16, bundle_get_slave(nab, i));
+ ds_put_format(s, "%"PRIu16, bundle->slaves[i]);
}
ds_put_cstr(s, ")");
-/* Copyright (c) 2011 Nicira, Inc.
+/* Copyright (c) 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.
struct ds;
struct flow;
+struct ofpact_bundle;
struct ofpbuf;
/* NXAST_BUNDLE helper functions.
*
* See include/openflow/nicira-ext.h for NXAST_BUNDLE specification. */
-uint16_t bundle_execute(const struct nx_action_bundle *, const struct flow *,
+uint16_t bundle_execute(const struct ofpact_bundle *, const struct flow *,
bool (*slave_enabled)(uint16_t ofp_port, void *aux),
void *aux);
-void bundle_execute_load(const struct nx_action_bundle *, struct flow *,
- bool (*slave_enabled)(uint16_t ofp_port, void *aux),
- void *aux);
-enum ofperr bundle_check(const struct nx_action_bundle *, int max_ports,
+enum ofperr bundle_from_openflow(const struct nx_action_bundle *,
+ struct ofpbuf *ofpact);
+enum ofperr bundle_check(const struct ofpact_bundle *, int max_ports,
const struct flow *);
-void bundle_parse(struct ofpbuf *, const char *);
-void bundle_parse_load(struct ofpbuf *b, const char *);
-void bundle_format(const struct nx_action_bundle *, struct ds *);
-
-/* Returns the 'i'th slave in 'nab'. */
-static inline uint16_t
-bundle_get_slave(const struct nx_action_bundle *nab, size_t i)
-{
- return ntohs(((ovs_be16 *)(nab + 1))[i]);
-}
+void bundle_to_nxast(const struct ofpact_bundle *, struct ofpbuf *of10);
+void bundle_parse(const char *, struct ofpbuf *ofpacts);
+void bundle_parse_load(const char *, struct ofpbuf *ofpacts);
+void bundle_format(const struct ofpact_bundle *, struct ds *);
#endif /* bundle.h */
/*
- * Copyright (c) 2008, 2009, 2010, 2011 Nicira, Inc.
+ * 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.
#define SENTINEL(N)
#endif
+/* ISO C says that a C implementation may choose any integer type for an enum
+ * that is sufficient to hold all of its values. Common ABIs (such as the
+ * System V ABI used on i386 GNU/Linux) always use a full-sized "int", even
+ * when a smaller type would suffice.
+ *
+ * In GNU C, "enum __attribute__((packed)) name { ... }" defines 'name' as an
+ * enum compatible with a type that is no bigger than necessary. This is the
+ * intended use of OVS_PACKED_ENUM.
+ *
+ * OVS_PACKED_ENUM is intended for use only as a space optimization, since it
+ * only works with GCC. That means that it must not be used in wire protocols
+ * or otherwise exposed outside of a single process. */
+#if __GNUC__ && !__CHECKER__
+#define OVS_PACKED_ENUM __attribute__((__packed__))
+#else
+#define OVS_PACKED_ENUM
+#endif
+
#endif /* compiler.h */
#include "dynamic-string.h"
#include "meta-flow.h"
#include "nx-match.h"
+#include "ofp-actions.h"
#include "ofp-errors.h"
#include "ofp-util.h"
#include "ofpbuf.h"
return value;
}
-static uint64_t
-get_bits(int n_bits, const void **p)
-{
- int n_segs = DIV_ROUND_UP(n_bits, 16);
- uint64_t value;
-
- value = 0;
- while (n_segs-- > 0) {
- value = (value << 16) | ntohs(get_be16(p));
- }
- return value;
-}
-
static void
get_subfield(int n_bits, const void **p, struct mf_subfield *sf)
{
return min_len;
}
-static enum ofperr
-learn_check_header(uint16_t header, size_t len)
+/* Converts 'nal' into a "struct ofpact_learn" and appends that struct to
+ * 'ofpacts'. Returns 0 if successful, otherwise an OFPERR_*. */
+enum ofperr
+learn_from_openflow(const struct nx_action_learn *nal, struct ofpbuf *ofpacts)
{
- int src_type = header & NX_LEARN_SRC_MASK;
- int dst_type = header & NX_LEARN_DST_MASK;
+ struct ofpact_learn *learn;
+ const void *p, *end;
- /* Check for valid src and dst type combination. */
- if (dst_type == NX_LEARN_DST_MATCH ||
- dst_type == NX_LEARN_DST_LOAD ||
- (dst_type == NX_LEARN_DST_OUTPUT &&
- src_type == NX_LEARN_SRC_FIELD)) {
- /* OK. */
- } else {
+ if (nal->pad) {
return OFPERR_OFPBAC_BAD_ARGUMENT;
}
- /* Check that the arguments don't overrun the end of the action. */
- if (len < learn_min_len(header)) {
- return OFPERR_OFPBAC_BAD_LEN;
- }
+ learn = ofpact_put_LEARN(ofpacts);
- return 0;
-}
-
-/* Checks that 'learn' (which must be at least 'sizeof *learn' bytes long) is a
- * valid action on 'flow'. */
-enum ofperr
-learn_check(const struct nx_action_learn *learn, const struct flow *flow)
-{
- struct cls_rule rule;
- const void *p, *end;
-
- cls_rule_init_catchall(&rule, 0);
+ learn->idle_timeout = ntohs(nal->idle_timeout);
+ learn->hard_timeout = ntohs(nal->hard_timeout);
+ learn->priority = ntohs(nal->priority);
+ learn->cookie = ntohll(nal->cookie);
+ learn->flags = ntohs(nal->flags);
+ learn->table_id = nal->table_id;
+ learn->fin_idle_timeout = ntohs(nal->fin_idle_timeout);
+ learn->fin_hard_timeout = ntohs(nal->fin_hard_timeout);
- if (learn->flags & ~htons(OFPFF_SEND_FLOW_REM)
- || learn->pad
- || learn->table_id == 0xff) {
+ if (learn->flags & ~OFPFF_SEND_FLOW_REM || learn->table_id == 0xff) {
return OFPERR_OFPBAC_BAD_ARGUMENT;
}
- end = (char *) learn + ntohs(learn->len);
- for (p = learn + 1; p != end; ) {
+ end = (char *) nal + ntohs(nal->len);
+ for (p = nal + 1; p != end; ) {
+ struct ofpact_learn_spec *spec;
uint16_t header = ntohs(get_be16(&p));
- int n_bits = header & NX_LEARN_N_BITS_MASK;
- int src_type = header & NX_LEARN_SRC_MASK;
- int dst_type = header & NX_LEARN_DST_MASK;
-
- enum ofperr error;
- uint64_t value;
if (!header) {
break;
}
- error = learn_check_header(header, (char *) end - (char *) p);
- if (error) {
- return error;
- }
+ spec = ofpbuf_put_zeros(ofpacts, sizeof *spec);
+ learn = ofpacts->l2;
+ learn->n_specs++;
- /* Check the source. */
- if (src_type == NX_LEARN_SRC_FIELD) {
- struct mf_subfield src;
+ spec->src_type = header & NX_LEARN_SRC_MASK;
+ spec->dst_type = header & NX_LEARN_DST_MASK;
+ spec->n_bits = header & NX_LEARN_N_BITS_MASK;
- get_subfield(n_bits, &p, &src);
- error = mf_check_src(&src, flow);
- if (error) {
- return error;
- }
- value = 0;
+ /* Check for valid src and dst type combination. */
+ if (spec->dst_type == NX_LEARN_DST_MATCH ||
+ spec->dst_type == NX_LEARN_DST_LOAD ||
+ (spec->dst_type == NX_LEARN_DST_OUTPUT &&
+ spec->src_type == NX_LEARN_SRC_FIELD)) {
+ /* OK. */
} else {
- value = get_bits(n_bits, &p);
+ return OFPERR_OFPBAC_BAD_ARGUMENT;
}
- /* Check the destination. */
- if (dst_type == NX_LEARN_DST_MATCH || dst_type == NX_LEARN_DST_LOAD) {
- struct mf_subfield dst;
+ /* Check that the arguments don't overrun the end of the action. */
+ if ((char *) end - (char *) p < learn_min_len(header)) {
+ return OFPERR_OFPBAC_BAD_LEN;
+ }
- get_subfield(n_bits, &p, &dst);
- error = (dst_type == NX_LEARN_DST_LOAD
- ? mf_check_dst(&dst, &rule.flow)
- : mf_check_src(&dst, &rule.flow));
- if (error) {
- return error;
- }
+ /* Get the source. */
+ if (spec->src_type == NX_LEARN_SRC_FIELD) {
+ get_subfield(spec->n_bits, &p, &spec->src);
+ } else {
+ int p_bytes = 2 * DIV_ROUND_UP(spec->n_bits, 16);
- if (dst_type == NX_LEARN_DST_MATCH
- && src_type == NX_LEARN_SRC_IMMEDIATE) {
- if (n_bits <= 64) {
- mf_set_subfield(&dst, value, &rule);
- } else {
- /* We're only setting subfields to allow us to check
- * prerequisites. No prerequisite depends on the value of
- * a field that is wider than 64 bits. So just skip
- * setting it entirely. */
- BUILD_ASSERT_DECL(FLOW_WC_SEQ == 12);
- }
- }
+ bitwise_copy(p, p_bytes, 0,
+ &spec->src_imm, sizeof spec->src_imm, 0,
+ spec->n_bits);
+ p = (const uint8_t *) p + p_bytes;
+ }
+
+ /* Get the destination. */
+ if (spec->dst_type == NX_LEARN_DST_MATCH ||
+ spec->dst_type == NX_LEARN_DST_LOAD) {
+ get_subfield(spec->n_bits, &p, &spec->dst);
}
}
+ ofpact_update_len(ofpacts, &learn->ofpact);
+
if (!is_all_zeros(p, (char *) end - (char *) p)) {
return OFPERR_OFPBAC_BAD_ARGUMENT;
}
return 0;
}
-void
-learn_execute(const struct nx_action_learn *learn, const struct flow *flow,
- struct ofputil_flow_mod *fm)
+/* Checks that 'learn' is a valid action on 'flow'. Returns 0 if it is valid,
+ * otherwise an OFPERR_*. */
+enum ofperr
+learn_check(const struct ofpact_learn *learn, const struct flow *flow)
{
- const void *p, *end;
- struct ofpbuf actions;
-
- cls_rule_init_catchall(&fm->cr, ntohs(learn->priority));
- fm->cookie = htonll(0);
- fm->cookie_mask = htonll(0);
- fm->new_cookie = learn->cookie;
- fm->table_id = learn->table_id;
- fm->command = OFPFC_MODIFY_STRICT;
- fm->idle_timeout = ntohs(learn->idle_timeout);
- fm->hard_timeout = ntohs(learn->hard_timeout);
- fm->buffer_id = UINT32_MAX;
- fm->out_port = OFPP_NONE;
- fm->flags = ntohs(learn->flags) & OFPFF_SEND_FLOW_REM;
- fm->actions = NULL;
- fm->n_actions = 0;
-
- ofpbuf_init(&actions, 64);
-
- if (learn->fin_idle_timeout || learn->fin_hard_timeout) {
- struct nx_action_fin_timeout *naft;
-
- naft = ofputil_put_NXAST_FIN_TIMEOUT(&actions);
- naft->fin_idle_timeout = learn->fin_idle_timeout;
- naft->fin_hard_timeout = learn->fin_hard_timeout;
- }
-
- for (p = learn + 1, end = (char *) learn + ntohs(learn->len); p != end; ) {
- uint16_t header = ntohs(get_be16(&p));
- int n_bits = header & NX_LEARN_N_BITS_MASK;
- int src_type = header & NX_LEARN_SRC_MASK;
- int dst_type = header & NX_LEARN_DST_MASK;
- union mf_subvalue value;
-
- struct mf_subfield dst;
- int chunk, ofs;
-
- if (!header) {
- break;
- }
-
- if (src_type == NX_LEARN_SRC_FIELD) {
- struct mf_subfield src;
+ const struct ofpact_learn_spec *spec;
+ struct cls_rule rule;
- get_subfield(n_bits, &p, &src);
- mf_read_subfield(&src, flow, &value);
- } else {
- int p_bytes = 2 * DIV_ROUND_UP(n_bits, 16);
+ cls_rule_init_catchall(&rule, 0);
+ for (spec = learn->specs; spec < &learn->specs[learn->n_specs]; spec++) {
+ enum ofperr error;
- memset(&value, 0, sizeof value);
- bitwise_copy(p, p_bytes, 0,
- &value, sizeof value, 0,
- n_bits);
- p = (const uint8_t *) p + p_bytes;
+ /* Check the source. */
+ if (spec->src_type == NX_LEARN_SRC_FIELD) {
+ error = mf_check_src(&spec->src, flow);
+ if (error) {
+ return error;
+ }
}
- switch (dst_type) {
+ /* Check the destination. */
+ switch (spec->dst_type) {
case NX_LEARN_DST_MATCH:
- get_subfield(n_bits, &p, &dst);
- mf_write_subfield(&dst, &value, &fm->cr);
+ error = mf_check_src(&spec->dst, &rule.flow);
+ if (error) {
+ return error;
+ }
+
+ mf_write_subfield(&spec->dst, &spec->src_imm, &rule);
break;
case NX_LEARN_DST_LOAD:
- get_subfield(n_bits, &p, &dst);
- for (ofs = 0; ofs < n_bits; ofs += chunk) {
- struct nx_action_reg_load *load;
-
- chunk = MIN(n_bits - ofs, 64);
-
- load = ofputil_put_NXAST_REG_LOAD(&actions);
- load->ofs_nbits = nxm_encode_ofs_nbits(dst.ofs + ofs, chunk);
- load->dst = htonl(dst.field->nxm_header);
- bitwise_copy(&value, sizeof value, ofs,
- &load->value, sizeof load->value, 0,
- chunk);
+ error = mf_check_dst(&spec->dst, &rule.flow);
+ if (error) {
+ return error;
}
break;
case NX_LEARN_DST_OUTPUT:
- if (n_bits <= 16 || is_all_zeros(value.u8, sizeof value - 2)) {
- ofputil_put_OFPAT10_OUTPUT(&actions)->port = value.be16[7];
- }
+ /* Nothing to do. */
break;
}
}
-
- fm->actions = ofpbuf_steal_data(&actions);
- fm->n_actions = actions.size / sizeof(struct ofp_action_header);
+ return 0;
}
static void
put_be32(b, htonl(x));
}
-struct learn_spec {
- int n_bits;
+/* Converts 'learn' into a "struct nx_action_learn" and appends that action to
+ * 'ofpacts'. */
+void
+learn_to_nxast(const struct ofpact_learn *learn, struct ofpbuf *openflow)
+{
+ const struct ofpact_learn_spec *spec;
+ struct nx_action_learn *nal;
+ size_t start_ofs;
+
+ start_ofs = openflow->size;
+ nal = ofputil_put_NXAST_LEARN(openflow);
+ nal->idle_timeout = htons(learn->idle_timeout);
+ nal->hard_timeout = htons(learn->hard_timeout);
+ nal->fin_idle_timeout = htons(learn->fin_idle_timeout);
+ nal->fin_hard_timeout = htons(learn->fin_hard_timeout);
+ nal->priority = htons(learn->priority);
+ nal->cookie = htonll(learn->cookie);
+ nal->flags = htons(learn->flags);
+ nal->table_id = learn->table_id;
+
+ for (spec = learn->specs; spec < &learn->specs[learn->n_specs]; spec++) {
+ put_u16(openflow, spec->n_bits | spec->dst_type | spec->src_type);
+
+ if (spec->src_type == NX_LEARN_SRC_FIELD) {
+ put_u32(openflow, spec->src.field->nxm_header);
+ put_u16(openflow, spec->src.ofs);
+ } else {
+ size_t n_dst_bytes = 2 * DIV_ROUND_UP(spec->n_bits, 16);
+ uint8_t *bits = ofpbuf_put_zeros(openflow, n_dst_bytes);
+ bitwise_copy(&spec->src_imm, sizeof spec->src_imm, 0,
+ bits, n_dst_bytes, 0,
+ spec->n_bits);
+ }
+
+ if (spec->dst_type == NX_LEARN_DST_MATCH ||
+ spec->dst_type == NX_LEARN_DST_LOAD) {
+ put_u32(openflow, spec->dst.field->nxm_header);
+ put_u16(openflow, spec->dst.ofs);
+ }
+ }
+
+ if ((openflow->size - start_ofs) % 8) {
+ ofpbuf_put_zeros(openflow, 8 - (openflow->size - start_ofs) % 8);
+ }
+
+ nal = ofpbuf_at_assert(openflow, start_ofs, sizeof *nal);
+ nal->len = htons(openflow->size - start_ofs);
+}
+
+/* Composes 'fm' so that executing it will implement 'learn' given that the
+ * packet being processed has 'flow' as its flow.
+ *
+ * Uses 'ofpacts' to store the flow mod's actions. The caller must initialize
+ * 'ofpacts' and retains ownership of it. 'fm->ofpacts' will point into the
+ * 'ofpacts' buffer.
+ *
+ * The caller has to actually execute 'fm'. */
+void
+learn_execute(const struct ofpact_learn *learn, const struct flow *flow,
+ struct ofputil_flow_mod *fm, struct ofpbuf *ofpacts)
+{
+ const struct ofpact_learn_spec *spec;
- int src_type;
- struct mf_subfield src;
- union mf_subvalue src_imm;
+ cls_rule_init_catchall(&fm->cr, learn->priority);
+ fm->cookie = htonll(0);
+ fm->cookie_mask = htonll(0);
+ fm->new_cookie = htonll(learn->cookie);
+ fm->table_id = learn->table_id;
+ fm->command = OFPFC_MODIFY_STRICT;
+ fm->idle_timeout = learn->idle_timeout;
+ fm->hard_timeout = learn->hard_timeout;
+ fm->buffer_id = UINT32_MAX;
+ fm->out_port = OFPP_NONE;
+ fm->flags = learn->flags;
+ fm->ofpacts = NULL;
+ fm->ofpacts_len = 0;
- int dst_type;
- struct mf_subfield dst;
-};
+ if (learn->fin_idle_timeout || learn->fin_hard_timeout) {
+ struct ofpact_fin_timeout *oft;
+
+ oft = ofpact_put_FIN_TIMEOUT(ofpacts);
+ oft->fin_idle_timeout = learn->fin_idle_timeout;
+ oft->fin_hard_timeout = learn->fin_hard_timeout;
+ }
+
+ for (spec = learn->specs; spec < &learn->specs[learn->n_specs]; spec++) {
+ union mf_subvalue value;
+ int chunk, ofs;
+
+ if (spec->src_type == NX_LEARN_SRC_FIELD) {
+ mf_read_subfield(&spec->src, flow, &value);
+ } else {
+ value = spec->src_imm;
+ }
+
+ switch (spec->dst_type) {
+ case NX_LEARN_DST_MATCH:
+ mf_write_subfield(&spec->dst, &value, &fm->cr);
+ break;
+
+ case NX_LEARN_DST_LOAD:
+ for (ofs = 0; ofs < spec->n_bits; ofs += chunk) {
+ struct ofpact_reg_load *load;
+ ovs_be64 value_be;
+
+ chunk = MIN(spec->n_bits - ofs, 64);
+
+ load = ofpact_put_REG_LOAD(ofpacts);
+ load->dst.field = spec->dst.field;
+ load->dst.ofs = spec->dst.ofs + ofs;
+ load->dst.n_bits = chunk;
+
+ memset(&value_be, 0, sizeof value_be);
+ bitwise_copy(&value, sizeof value, ofs,
+ &value_be, sizeof value_be, 0,
+ chunk);
+ load->value = ntohll(value_be);
+ }
+ break;
+
+ case NX_LEARN_DST_OUTPUT:
+ if (spec->n_bits <= 16
+ || is_all_zeros(value.u8, sizeof value - 2)) {
+ uint16_t port = ntohs(value.be16[7]);
+
+ if (port < OFPP_MAX
+ || port == OFPP_IN_PORT
+ || port == OFPP_FLOOD
+ || port == OFPP_LOCAL
+ || port == OFPP_ALL) {
+ ofpact_put_OUTPUT(ofpacts)->port = port;
+ }
+ }
+ break;
+ }
+ }
+ ofpact_pad(ofpacts);
+
+ fm->ofpacts = ofpacts->data;
+ fm->ofpacts_len = ofpacts->size;
+}
static void
-learn_parse_load_immediate(const char *s, struct learn_spec *spec)
+learn_parse_load_immediate(const char *s, struct ofpact_learn_spec *spec)
{
const char *full_s = s;
const char *arrow = strstr(s, "->");
static void
learn_parse_spec(const char *orig, char *name, char *value,
- struct learn_spec *spec)
+ struct ofpact_learn_spec *spec)
{
- memset(spec, 0, sizeof *spec);
if (mf_from_name(name)) {
const struct mf_field *dst = mf_from_name(name);
union mf_value imm;
if (value[strcspn(value, "[-")] == '-') {
learn_parse_load_immediate(value, spec);
} else {
- struct nx_action_reg_move move;
+ struct ofpact_reg_move move;
nxm_parse_reg_move(&move, value);
- spec->n_bits = ntohs(move.n_bits);
+ spec->n_bits = move.src.n_bits;
spec->src_type = NX_LEARN_SRC_FIELD;
- nxm_decode_discrete(&spec->src,
- move.src, move.src_ofs, move.n_bits);
+ spec->src = move.src;
spec->dst_type = NX_LEARN_DST_LOAD;
- nxm_decode_discrete(&spec->dst,
- move.dst, move.dst_ofs, move.n_bits);
+ spec->dst = move.dst;
}
} else if (!strcmp(name, "output")) {
if (mf_parse_subfield(&spec->src, value)[0] != '\0') {
}
/* Parses 'arg' as a set of arguments to the "learn" action and appends a
- * matching NXAST_LEARN action to 'b'. The format parsed is described in
- * ovs-ofctl(8).
+ * matching OFPACT_LEARN action to 'ofpacts'. ovs-ofctl(8) describes the
+ * format parsed.
*
* Prints an error on stderr and aborts the program if 'arg' syntax is invalid.
*
*
* Modifies 'arg'. */
void
-learn_parse(struct ofpbuf *b, char *arg, const struct flow *flow)
+learn_parse(char *arg, const struct flow *flow, struct ofpbuf *ofpacts)
{
char *orig = xstrdup(arg);
char *name, *value;
- enum ofperr error;
- size_t learn_ofs;
- size_t len;
- struct nx_action_learn *learn;
+ struct ofpact_learn *learn;
struct cls_rule rule;
+ enum ofperr error;
- learn_ofs = b->size;
- learn = ofputil_put_NXAST_LEARN(b);
- learn->idle_timeout = htons(OFP_FLOW_PERMANENT);
- learn->hard_timeout = htons(OFP_FLOW_PERMANENT);
- learn->priority = htons(OFP_DEFAULT_PRIORITY);
- learn->cookie = htonll(0);
- learn->flags = htons(0);
+ learn = ofpact_put_LEARN(ofpacts);
+ learn->idle_timeout = OFP_FLOW_PERMANENT;
+ learn->hard_timeout = OFP_FLOW_PERMANENT;
+ learn->priority = OFP_DEFAULT_PRIORITY;
learn->table_id = 1;
cls_rule_init_catchall(&rule, 0);
while (ofputil_parse_key_value(&arg, &name, &value)) {
- learn = ofpbuf_at_assert(b, learn_ofs, sizeof *learn);
if (!strcmp(name, "table")) {
learn->table_id = atoi(value);
if (learn->table_id == 255) {
orig);
}
} else if (!strcmp(name, "priority")) {
- learn->priority = htons(atoi(value));
+ learn->priority = atoi(value);
} else if (!strcmp(name, "idle_timeout")) {
- learn->idle_timeout = htons(atoi(value));
+ learn->idle_timeout = atoi(value);
} else if (!strcmp(name, "hard_timeout")) {
- learn->hard_timeout = htons(atoi(value));
+ learn->hard_timeout = atoi(value);
} else if (!strcmp(name, "fin_idle_timeout")) {
- learn->fin_idle_timeout = htons(atoi(value));
+ learn->fin_idle_timeout = atoi(value);
} else if (!strcmp(name, "fin_hard_timeout")) {
- learn->fin_hard_timeout = htons(atoi(value));
+ learn->fin_hard_timeout = atoi(value);
} else if (!strcmp(name, "cookie")) {
- learn->cookie = htonll(strtoull(value, NULL, 0));
+ learn->cookie = strtoull(value, NULL, 0);
} else {
- struct learn_spec spec;
+ struct ofpact_learn_spec *spec;
+
+ spec = ofpbuf_put_zeros(ofpacts, sizeof *spec);
+ learn = ofpacts->l2;
+ learn->n_specs++;
- learn_parse_spec(orig, name, value, &spec);
+ learn_parse_spec(orig, name, value, spec);
/* Check prerequisites. */
- if (spec.src_type == NX_LEARN_SRC_FIELD
- && flow && !mf_are_prereqs_ok(spec.src.field, flow)) {
+ if (spec->src_type == NX_LEARN_SRC_FIELD
+ && flow && !mf_are_prereqs_ok(spec->src.field, flow)) {
ovs_fatal(0, "%s: cannot specify source field %s because "
"prerequisites are not satisfied",
- orig, spec.src.field->name);
+ orig, spec->src.field->name);
}
- if ((spec.dst_type == NX_LEARN_DST_MATCH
- || spec.dst_type == NX_LEARN_DST_LOAD)
- && !mf_are_prereqs_ok(spec.dst.field, &rule.flow)) {
+ if ((spec->dst_type == NX_LEARN_DST_MATCH
+ || spec->dst_type == NX_LEARN_DST_LOAD)
+ && !mf_are_prereqs_ok(spec->dst.field, &rule.flow)) {
ovs_fatal(0, "%s: cannot specify destination field %s because "
"prerequisites are not satisfied",
- orig, spec.dst.field->name);
+ orig, spec->dst.field->name);
}
/* Update 'rule' to allow for satisfying destination
* prerequisites. */
- if (spec.src_type == NX_LEARN_SRC_IMMEDIATE
- && spec.dst_type == NX_LEARN_DST_MATCH) {
- mf_write_subfield(&spec.dst, &spec.src_imm, &rule);
- }
-
- /* Output the flow_mod_spec. */
- put_u16(b, spec.n_bits | spec.src_type | spec.dst_type);
- if (spec.src_type == NX_LEARN_SRC_IMMEDIATE) {
- int n_bytes = DIV_ROUND_UP(spec.n_bits, 16) * 2;
- int ofs = sizeof spec.src_imm - n_bytes;
- ofpbuf_put(b, &spec.src_imm.u8[ofs], n_bytes);
- } else {
- put_u32(b, spec.src.field->nxm_header);
- put_u16(b, spec.src.ofs);
- }
- if (spec.dst_type == NX_LEARN_DST_MATCH ||
- spec.dst_type == NX_LEARN_DST_LOAD) {
- put_u32(b, spec.dst.field->nxm_header);
- put_u16(b, spec.dst.ofs);
- } else {
- assert(spec.dst_type == NX_LEARN_DST_OUTPUT);
+ if (spec->src_type == NX_LEARN_SRC_IMMEDIATE
+ && spec->dst_type == NX_LEARN_DST_MATCH) {
+ mf_write_subfield(&spec->dst, &spec->src_imm, &rule);
}
}
}
-
- put_u16(b, 0);
-
- len = b->size - learn_ofs;
- if (len % 8) {
- ofpbuf_put_zeros(b, 8 - len % 8);
- }
-
- learn = ofpbuf_at_assert(b, learn_ofs, sizeof *learn);
- learn->len = htons(b->size - learn_ofs);
+ ofpact_update_len(ofpacts, &learn->ofpact);
/* In theory the above should have caught any errors, but... */
if (flow) {
free(orig);
}
+static void
+format_subvalue(const union mf_subvalue *subvalue, struct ds *s)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(subvalue->u8); i++) {
+ if (subvalue->u8[i]) {
+ ds_put_format(s, "0x%"PRIx8, subvalue->u8[i]);
+ for (i++; i < ARRAY_SIZE(subvalue->u8); i++) {
+ ds_put_format(s, "%02"PRIx8, subvalue->u8[i]);
+ }
+ return;
+ }
+ }
+ ds_put_char(s, '0');
+}
+
+/* Appends a description of 'learn' to 's', in the format that ovs-ofctl(8)
+ * describes. */
void
-learn_format(const struct nx_action_learn *learn, struct ds *s)
+learn_format(const struct ofpact_learn *learn, struct ds *s)
{
+ const struct ofpact_learn_spec *spec;
struct cls_rule rule;
- const void *p, *end;
cls_rule_init_catchall(&rule, 0);
ds_put_format(s, "learn(table=%"PRIu8, learn->table_id);
- if (learn->idle_timeout != htons(OFP_FLOW_PERMANENT)) {
- ds_put_format(s, ",idle_timeout=%"PRIu16, ntohs(learn->idle_timeout));
+ if (learn->idle_timeout != OFP_FLOW_PERMANENT) {
+ ds_put_format(s, ",idle_timeout=%"PRIu16, learn->idle_timeout);
}
- if (learn->hard_timeout != htons(OFP_FLOW_PERMANENT)) {
- ds_put_format(s, ",hard_timeout=%"PRIu16, ntohs(learn->hard_timeout));
+ if (learn->hard_timeout != OFP_FLOW_PERMANENT) {
+ ds_put_format(s, ",hard_timeout=%"PRIu16, learn->hard_timeout);
}
if (learn->fin_idle_timeout) {
- ds_put_format(s, ",fin_idle_timeout=%"PRIu16,
- ntohs(learn->fin_idle_timeout));
+ ds_put_format(s, ",fin_idle_timeout=%"PRIu16, learn->fin_idle_timeout);
}
if (learn->fin_hard_timeout) {
- ds_put_format(s, ",fin_hard_timeout=%"PRIu16,
- ntohs(learn->fin_hard_timeout));
+ ds_put_format(s, ",fin_hard_timeout=%"PRIu16, learn->fin_hard_timeout);
}
- if (learn->priority != htons(OFP_DEFAULT_PRIORITY)) {
- ds_put_format(s, ",priority=%"PRIu16, ntohs(learn->priority));
+ if (learn->priority != OFP_DEFAULT_PRIORITY) {
+ ds_put_format(s, ",priority=%"PRIu16, learn->priority);
}
- if (learn->flags & htons(OFPFF_SEND_FLOW_REM)) {
+ if (learn->flags & OFPFF_SEND_FLOW_REM) {
ds_put_cstr(s, ",OFPFF_SEND_FLOW_REM");
}
- if (learn->flags & htons(~OFPFF_SEND_FLOW_REM)) {
- ds_put_format(s, ",***flags=%"PRIu16"***",
- ntohs(learn->flags) & ~OFPFF_SEND_FLOW_REM);
+ if (learn->cookie != 0) {
+ ds_put_format(s, ",cookie=%#"PRIx64, learn->cookie);
}
- if (learn->cookie != htonll(0)) {
- ds_put_format(s, ",cookie=0x%"PRIx64, ntohll(learn->cookie));
- }
- if (learn->pad != 0) {
- ds_put_cstr(s, ",***nonzero pad***");
- }
-
- end = (char *) learn + ntohs(learn->len);
- for (p = learn + 1; p != end; ) {
- uint16_t header = ntohs(get_be16(&p));
- int n_bits = header & NX_LEARN_N_BITS_MASK;
-
- int src_type = header & NX_LEARN_SRC_MASK;
- struct mf_subfield src;
- const uint8_t *src_value;
- int src_value_bytes;
-
- int dst_type = header & NX_LEARN_DST_MASK;
- struct mf_subfield dst;
-
- enum ofperr error;
- int i;
-
- if (!header) {
- break;
- }
-
- error = learn_check_header(header, (char *) end - (char *) p);
- if (error == OFPERR_OFPBAC_BAD_ARGUMENT) {
- ds_put_format(s, ",***bad flow_mod_spec header %"PRIx16"***)",
- header);
- return;
- } else if (error == OFPERR_OFPBAC_BAD_LEN) {
- ds_put_format(s, ",***flow_mod_spec at offset %td is %u bytes "
- "long but only %td bytes are left***)",
- (char *) p - (char *) (learn + 1) - 2,
- learn_min_len(header) + 2,
- (char *) end - (char *) p + 2);
- return;
- }
- assert(!error);
-
- /* Get the source. */
- if (src_type == NX_LEARN_SRC_FIELD) {
- get_subfield(n_bits, &p, &src);
- src_value_bytes = 0;
- src_value = NULL;
- } else {
- src.field = NULL;
- src.ofs = 0;
- src.n_bits = 0;
- src_value_bytes = 2 * DIV_ROUND_UP(n_bits, 16);
- src_value = p;
- p = (const void *) ((const uint8_t *) p + src_value_bytes);
- }
-
- /* Get the destination. */
- if (dst_type == NX_LEARN_DST_MATCH || dst_type == NX_LEARN_DST_LOAD) {
- get_subfield(n_bits, &p, &dst);
- } else {
- dst.field = NULL;
- dst.ofs = 0;
- dst.n_bits = 0;
- }
+ for (spec = learn->specs; spec < &learn->specs[learn->n_specs]; spec++) {
ds_put_char(s, ',');
- switch (src_type | dst_type) {
+ switch (spec->src_type | spec->dst_type) {
case NX_LEARN_SRC_IMMEDIATE | NX_LEARN_DST_MATCH:
- if (dst.field && dst.ofs == 0 && n_bits == dst.field->n_bits) {
+ if (spec->dst.ofs == 0
+ && spec->dst.n_bits == spec->dst.field->n_bits) {
union mf_value value;
- uint8_t *bytes = (uint8_t *) &value;
-
- if (src_value_bytes > dst.field->n_bytes) {
- /* The destination field is an odd number of bytes, which
- * got rounded up to a multiple of 2 to be put into the
- * learning action. Skip over the leading byte, which
- * should be zero anyway. Otherwise the memcpy() below
- * will overrun the start of 'value'. */
- int diff = src_value_bytes - dst.field->n_bytes;
- src_value += diff;
- src_value_bytes -= diff;
- }
memset(&value, 0, sizeof value);
- memcpy(&bytes[dst.field->n_bytes - src_value_bytes],
- src_value, src_value_bytes);
- ds_put_format(s, "%s=", dst.field->name);
- mf_format(dst.field, &value, NULL, s);
+ bitwise_copy(&spec->src_imm, sizeof spec->src_imm, 0,
+ &value, spec->dst.field->n_bytes, 0,
+ spec->dst.field->n_bits);
+ ds_put_format(s, "%s=", spec->dst.field->name);
+ mf_format(spec->dst.field, &value, NULL, s);
} else {
- mf_format_subfield(&dst, s);
- ds_put_cstr(s, "=0x");
- for (i = 0; i < src_value_bytes; i++) {
- ds_put_format(s, "%02"PRIx8, src_value[i]);
- }
+ mf_format_subfield(&spec->dst, s);
+ ds_put_char(s, '=');
+ format_subvalue(&spec->src_imm, s);
}
break;
case NX_LEARN_SRC_FIELD | NX_LEARN_DST_MATCH:
- mf_format_subfield(&dst, s);
- if (src.field != dst.field || src.ofs != dst.ofs) {
+ mf_format_subfield(&spec->dst, s);
+ if (spec->src.field != spec->dst.field ||
+ spec->src.ofs != spec->dst.ofs) {
ds_put_char(s, '=');
- mf_format_subfield(&src, s);
+ mf_format_subfield(&spec->src, s);
}
break;
case NX_LEARN_SRC_IMMEDIATE | NX_LEARN_DST_LOAD:
- ds_put_cstr(s, "load:0x");
- for (i = 0; i < src_value_bytes; i++) {
- ds_put_format(s, "%02"PRIx8, src_value[i]);
- }
+ ds_put_format(s, "load:");
+ format_subvalue(&spec->src_imm, s);
ds_put_cstr(s, "->");
- mf_format_subfield(&dst, s);
+ mf_format_subfield(&spec->dst, s);
break;
case NX_LEARN_SRC_FIELD | NX_LEARN_DST_LOAD:
ds_put_cstr(s, "load:");
- mf_format_subfield(&src, s);
+ mf_format_subfield(&spec->src, s);
ds_put_cstr(s, "->");
- mf_format_subfield(&dst, s);
+ mf_format_subfield(&spec->dst, s);
break;
case NX_LEARN_SRC_FIELD | NX_LEARN_DST_OUTPUT:
ds_put_cstr(s, "output:");
- mf_format_subfield(&src, s);
+ mf_format_subfield(&spec->src, s);
break;
}
}
- if (!is_all_zeros(p, (char *) end - (char *) p)) {
- ds_put_cstr(s, ",***nonzero trailer***");
- }
ds_put_char(s, ')');
}
/*
- * Copyright (c) 2011 Nicira, Inc.
+ * Copyright (c) 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.
struct ds;
struct flow;
struct ofpbuf;
+struct ofpact_learn;
struct ofputil_flow_mod;
struct nx_action_learn;
* See include/openflow/nicira-ext.h for NXAST_LEARN specification.
*/
-enum ofperr learn_check(const struct nx_action_learn *, const struct flow *);
-void learn_execute(const struct nx_action_learn *, const struct flow *,
- struct ofputil_flow_mod *);
+enum ofperr learn_from_openflow(const struct nx_action_learn *,
+ struct ofpbuf *ofpacts);
+enum ofperr learn_check(const struct ofpact_learn *, const struct flow *);
+void learn_to_nxast(const struct ofpact_learn *, struct ofpbuf *openflow);
-void learn_parse(struct ofpbuf *, char *, const struct flow *);
-void learn_format(const struct nx_action_learn *, struct ds *);
+void learn_execute(const struct ofpact_learn *, const struct flow *,
+ struct ofputil_flow_mod *, struct ofpbuf *ofpacts);
+
+void learn_parse(char *, const struct flow *, struct ofpbuf *ofpacts);
+void learn_format(const struct ofpact_learn *, struct ds *);
#endif /* learn.h */
#include "hmap.h"
#include "mac-learning.h"
#include "ofpbuf.h"
+#include "ofp-actions.h"
#include "ofp-errors.h"
#include "ofp-parse.h"
#include "ofp-print.h"
* Otherwise, the switch processes every packet. */
int max_idle;
+ enum ofputil_protocol protocol;
unsigned long long int datapath_id;
time_t last_features_request;
struct mac_learning *ml; /* NULL to act as hub instead of switch. */
static enum ofperr process_switch_features(struct lswitch *,
struct ofp_switch_features *);
static void process_packet_in(struct lswitch *, struct rconn *,
- const struct ofp_packet_in *);
+ const struct ofp_header *);
static void process_echo_request(struct lswitch *, struct rconn *,
const struct ofp_header *);
struct lswitch *
lswitch_create(struct rconn *rconn, const struct lswitch_config *cfg)
{
+ enum ofputil_protocol protocol;
struct lswitch *sw;
sw = xzalloc(sizeof *sw);
sw->queued = rconn_packet_counter_create();
send_features_request(sw, rconn);
+ protocol = ofputil_protocol_from_ofp_version(rconn_get_version(rconn));
if (cfg->default_flows) {
enum ofputil_protocol usable_protocols;
- enum ofputil_protocol protocol;
struct ofpbuf *msg = NULL;
- int ofp_version;
int error = 0;
size_t i;
- /* Figure out the initial protocol on the connection. */
- ofp_version = rconn_get_version(rconn);
- protocol = ofputil_protocol_from_ofp_version(ofp_version);
-
/* If the initial protocol isn't good enough for default_flows, then
* pick one that will work and encode messages to set up that
* protocol.
rconn_get_name(rconn), strerror(error));
}
}
+ sw->protocol = protocol;
return sw;
}
break;
case OFPUTIL_OFPT_PACKET_IN:
+ case OFPUTIL_NXT_PACKET_IN:
process_packet_in(sw, rconn, msg->data);
break;
case OFPUTIL_NXT_FLOW_MOD_TABLE_ID:
case OFPUTIL_NXT_SET_FLOW_FORMAT:
case OFPUTIL_NXT_SET_PACKET_IN_FORMAT:
- case OFPUTIL_NXT_PACKET_IN:
case OFPUTIL_NXT_FLOW_MOD:
case OFPUTIL_NXT_FLOW_REMOVED:
case OFPUTIL_NXT_FLOW_AGE:
static void
process_packet_in(struct lswitch *sw, struct rconn *rconn,
- const struct ofp_packet_in *opi)
+ const struct ofp_header *oh)
{
- uint16_t in_port = ntohs(opi->in_port);
+ struct ofputil_packet_in pi;
uint32_t queue_id;
uint16_t out_port;
- struct ofp_action_header actions[2];
- size_t actions_len;
+ uint64_t ofpacts_stub[64 / 8];
+ struct ofpbuf ofpacts;
struct ofputil_packet_out po;
+ enum ofperr error;
- size_t pkt_ofs, pkt_len;
struct ofpbuf pkt;
struct flow flow;
+ error = ofputil_decode_packet_in(&pi, oh);
+ if (error) {
+ VLOG_WARN_RL(&rl, "failed to decode packet-in: %s",
+ ofperr_to_string(error));
+ return;
+ }
+
/* Ignore packets sent via output to OFPP_CONTROLLER. This library never
* uses such an action. You never know what experiments might be going on,
* though, and it seems best not to interfere with them. */
- if (opi->reason != OFPR_NO_MATCH) {
+ if (pi.reason != OFPR_NO_MATCH) {
return;
}
/* Extract flow data from 'opi' into 'flow'. */
- pkt_ofs = offsetof(struct ofp_packet_in, data);
- pkt_len = ntohs(opi->header.length) - pkt_ofs;
- ofpbuf_use_const(&pkt, opi->data, pkt_len);
- flow_extract(&pkt, 0, 0, in_port, &flow);
+ ofpbuf_use_const(&pkt, pi.packet, pi.packet_len);
+ flow_extract(&pkt, 0, pi.fmd.tun_id, pi.fmd.in_port, &flow);
/* Choose output port. */
out_port = lswitch_choose_destination(sw, &flow);
/* Make actions. */
- queue_id = get_queue_id(sw, in_port);
+ queue_id = get_queue_id(sw, pi.fmd.in_port);
+ ofpbuf_use_stack(&ofpacts, ofpacts_stub, sizeof ofpacts_stub);
if (out_port == OFPP_NONE) {
- actions_len = 0;
+ /* No actions. */
} else if (queue_id == UINT32_MAX || out_port >= OFPP_MAX) {
- struct ofp_action_output oao;
-
- memset(&oao, 0, sizeof oao);
- oao.type = htons(OFPAT10_OUTPUT);
- oao.len = htons(sizeof oao);
- oao.port = htons(out_port);
-
- memcpy(actions, &oao, sizeof oao);
- actions_len = sizeof oao;
+ ofpact_put_OUTPUT(&ofpacts)->port = out_port;
} else {
- struct ofp_action_enqueue oae;
-
- memset(&oae, 0, sizeof oae);
- oae.type = htons(OFPAT10_ENQUEUE);
- oae.len = htons(sizeof oae);
- oae.port = htons(out_port);
- oae.queue_id = htonl(queue_id);
-
- memcpy(actions, &oae, sizeof oae);
- actions_len = sizeof oae;
+ struct ofpact_enqueue *enqueue = ofpact_put_ENQUEUE(&ofpacts);
+ enqueue->port = out_port;
+ enqueue->queue = queue_id;
}
- assert(actions_len <= sizeof actions);
+ ofpact_pad(&ofpacts);
/* Prepare packet_out in case we need one. */
- po.buffer_id = ntohl(opi->buffer_id);
+ po.buffer_id = pi.buffer_id;
if (po.buffer_id == UINT32_MAX) {
po.packet = pkt.data;
po.packet_len = pkt.size;
po.packet = NULL;
po.packet_len = 0;
}
- po.in_port = in_port;
- po.actions = (union ofp_action *) actions;
- po.n_actions = actions_len / sizeof *actions;
+ po.in_port = pi.fmd.in_port;
+ po.ofpacts = ofpacts.data;
+ po.ofpacts_len = ofpacts.size;
/* Send the packet, and possibly the whole flow, to the output port. */
if (sw->max_idle >= 0 && (!sw->ml || out_port != OFPP_FLOOD)) {
+ struct ofputil_flow_mod fm;
struct ofpbuf *buffer;
- struct cls_rule rule;
/* The output port is known, or we always flood everything, so add a
* new flow. */
- cls_rule_init(&flow, &sw->wc, 0, &rule);
- buffer = make_add_flow(&rule, ntohl(opi->buffer_id),
- sw->max_idle, actions_len);
- ofpbuf_put(buffer, actions, actions_len);
+ memset(&fm, 0, sizeof fm);
+ cls_rule_init(&flow, &sw->wc, 0, &fm.cr);
+ fm.table_id = 0xff;
+ fm.command = OFPFC_ADD;
+ fm.idle_timeout = sw->max_idle;
+ fm.buffer_id = pi.buffer_id;
+ fm.out_port = OFPP_NONE;
+ fm.ofpacts = ofpacts.data;
+ fm.ofpacts_len = ofpacts.size;
+ buffer = ofputil_encode_flow_mod(&fm, sw->protocol);
+
queue_tx(sw, rconn, buffer);
/* If the switch didn't buffer the packet, we need to send a copy. */
- if (ntohl(opi->buffer_id) == UINT32_MAX && actions_len > 0) {
+ if (pi.buffer_id == UINT32_MAX && out_port != OFPP_NONE) {
queue_tx(sw, rconn, ofputil_encode_packet_out(&po));
}
} else {
/* We don't know that MAC, or we don't set up flows. Send along the
* packet without setting up a flow. */
- if (ntohl(opi->buffer_id) != UINT32_MAX || actions_len > 0) {
+ if (pi.buffer_id != UINT32_MAX || out_port != OFPP_NONE) {
queue_tx(sw, rconn, ofputil_encode_packet_out(&po));
}
}
#include <sys/types.h>
#include <netinet/in.h>
#include "dynamic-string.h"
-#include "meta-flow.h"
#include "nx-match.h"
+#include "ofp-actions.h"
#include "ofp-errors.h"
#include "ofp-util.h"
#include "openflow/nicira-ext.h"
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
\f
-/* multipath_check(). */
+/* Converts 'nam' into 'mp'. Returns 0 if successful, otherwise an
+ * OFPERR_*. */
enum ofperr
-multipath_check(const struct nx_action_multipath *mp, const struct flow *flow)
+multipath_from_openflow(const struct nx_action_multipath *nam,
+ struct ofpact_multipath *mp)
{
- uint32_t n_links = ntohs(mp->max_link) + 1;
+ uint32_t n_links = ntohs(nam->max_link) + 1;
size_t min_n_bits = log_2_ceil(n_links);
- struct mf_subfield dst;
- enum ofperr error;
- nxm_decode(&dst, mp->dst, mp->ofs_nbits);
- error = mf_check_dst(&dst, flow);
- if (error) {
- return error;
- }
-
- if (!flow_hash_fields_valid(ntohs(mp->fields))) {
- VLOG_WARN_RL(&rl, "unsupported fields %"PRIu16, ntohs(mp->fields));
- } else if (mp->algorithm != htons(NX_MP_ALG_MODULO_N)
- && mp->algorithm != htons(NX_MP_ALG_HASH_THRESHOLD)
- && mp->algorithm != htons(NX_MP_ALG_HRW)
- && mp->algorithm != htons(NX_MP_ALG_ITER_HASH)) {
- VLOG_WARN_RL(&rl, "unsupported algorithm %"PRIu16,
- ntohs(mp->algorithm));
- } else if (dst.n_bits < min_n_bits) {
+ ofpact_init_MULTIPATH(mp);
+ mp->fields = ntohs(nam->fields);
+ mp->basis = ntohs(nam->basis);
+ mp->algorithm = ntohs(nam->algorithm);
+ mp->max_link = ntohs(nam->max_link);
+ mp->arg = ntohl(nam->arg);
+ mp->dst.field = mf_from_nxm_header(ntohl(nam->dst));
+ mp->dst.ofs = nxm_decode_ofs(nam->ofs_nbits);
+ mp->dst.n_bits = nxm_decode_n_bits(nam->ofs_nbits);
+
+ if (!flow_hash_fields_valid(mp->fields)) {
+ VLOG_WARN_RL(&rl, "unsupported fields %d", (int) mp->fields);
+ return OFPERR_OFPBAC_BAD_ARGUMENT;
+ } else if (mp->algorithm != NX_MP_ALG_MODULO_N
+ && mp->algorithm != NX_MP_ALG_HASH_THRESHOLD
+ && mp->algorithm != NX_MP_ALG_HRW
+ && mp->algorithm != NX_MP_ALG_ITER_HASH) {
+ VLOG_WARN_RL(&rl, "unsupported algorithm %d", (int) mp->algorithm);
+ return OFPERR_OFPBAC_BAD_ARGUMENT;
+ } else if (mp->dst.n_bits < min_n_bits) {
VLOG_WARN_RL(&rl, "multipath action requires at least %zu bits for "
"%"PRIu32" links", min_n_bits, n_links);
- } else {
- return 0;
+ return OFPERR_OFPBAC_BAD_ARGUMENT;
}
- return OFPERR_OFPBAC_BAD_ARGUMENT;
+ return multipath_check(mp, NULL);
+}
+
+/* Checks that 'mp' is valid on flow. Returns 0 if it is valid, otherwise an
+ * OFPERR_*. */
+enum ofperr
+multipath_check(const struct ofpact_multipath *mp,
+ const struct flow *flow)
+{
+ return mf_check_dst(&mp->dst, flow);
+}
+
+/* Converts 'mp' into an OpenFlow NXAST_MULTIPATH action, which it appends to
+ * 'openflow'. */
+void
+multipath_to_nxast(const struct ofpact_multipath *mp, struct ofpbuf *openflow)
+{
+ struct nx_action_multipath *nam = ofputil_put_NXAST_MULTIPATH(openflow);
+
+ nam->fields = htons(mp->fields);
+ nam->basis = htons(mp->basis);
+ nam->algorithm = htons(mp->algorithm);
+ nam->max_link = htons(mp->max_link);
+ nam->arg = htonl(mp->arg);
+ nam->ofs_nbits = nxm_encode_ofs_nbits(mp->dst.ofs, mp->dst.n_bits);
+ nam->dst = htonl(mp->dst.field->nxm_header);
}
\f
/* multipath_execute(). */
static uint16_t multipath_algorithm(uint32_t hash, enum nx_mp_algorithm,
unsigned int n_links, unsigned int arg);
+/* Executes 'mp' based on the current contents of 'flow', writing the results
+ * back into 'flow'. */
void
-multipath_execute(const struct nx_action_multipath *mp, struct flow *flow)
+multipath_execute(const struct ofpact_multipath *mp, struct flow *flow)
{
/* Calculate value to store. */
- uint32_t hash = flow_hash_fields(flow, ntohs(mp->fields),
- ntohs(mp->basis));
- uint16_t link = multipath_algorithm(hash, ntohs(mp->algorithm),
- ntohs(mp->max_link) + 1,
- ntohl(mp->arg));
- struct mf_subfield dst;
-
- nxm_decode(&dst, mp->dst, mp->ofs_nbits);
- mf_set_subfield_value(&dst, link, flow);
+ uint32_t hash = flow_hash_fields(flow, mp->fields, mp->basis);
+ uint16_t link = multipath_algorithm(hash, mp->algorithm,
+ mp->max_link + 1, mp->arg);
+
+ nxm_reg_load(&mp->dst, link, flow);
}
static uint16_t
NOT_REACHED();
}
\f
-/* multipath_parse(). */
-
+/* Parses 's_' as a set of arguments to the "multipath" action and initializes
+ * 'mp' accordingly. ovs-ofctl(8) describes the format parsed.
+ *
+ * Prints an error on stderr and aborts the program if 's_' syntax is
+ * invalid. */
void
-multipath_parse(struct nx_action_multipath *mp, const char *s_)
+multipath_parse(struct ofpact_multipath *mp, const char *s_)
{
char *s = xstrdup(s_);
char *save_ptr = NULL;
- char *fields, *basis, *algorithm, *n_links_str, *arg, *dst_s;
- struct mf_subfield dst;
+ char *fields, *basis, *algorithm, *n_links_str, *arg, *dst;
int n_links;
fields = strtok_r(s, ", ", &save_ptr);
algorithm = strtok_r(NULL, ", ", &save_ptr);
n_links_str = strtok_r(NULL, ", ", &save_ptr);
arg = strtok_r(NULL, ", ", &save_ptr);
- dst_s = strtok_r(NULL, ", ", &save_ptr);
- if (!dst_s) {
+ dst = strtok_r(NULL, ", ", &save_ptr);
+ if (!dst) {
ovs_fatal(0, "%s: not enough arguments to multipath action", s_);
}
- ofputil_init_NXAST_MULTIPATH(mp);
+ ofpact_init_MULTIPATH(mp);
if (!strcasecmp(fields, "eth_src")) {
- mp->fields = htons(NX_HASH_FIELDS_ETH_SRC);
+ mp->fields = NX_HASH_FIELDS_ETH_SRC;
} else if (!strcasecmp(fields, "symmetric_l4")) {
- mp->fields = htons(NX_HASH_FIELDS_SYMMETRIC_L4);
+ mp->fields = NX_HASH_FIELDS_SYMMETRIC_L4;
} else {
ovs_fatal(0, "%s: unknown fields `%s'", s_, fields);
}
- mp->basis = htons(atoi(basis));
+ mp->basis = atoi(basis);
if (!strcasecmp(algorithm, "modulo_n")) {
- mp->algorithm = htons(NX_MP_ALG_MODULO_N);
+ mp->algorithm = NX_MP_ALG_MODULO_N;
} else if (!strcasecmp(algorithm, "hash_threshold")) {
- mp->algorithm = htons(NX_MP_ALG_HASH_THRESHOLD);
+ mp->algorithm = NX_MP_ALG_HASH_THRESHOLD;
} else if (!strcasecmp(algorithm, "hrw")) {
- mp->algorithm = htons(NX_MP_ALG_HRW);
+ mp->algorithm = NX_MP_ALG_HRW;
} else if (!strcasecmp(algorithm, "iter_hash")) {
- mp->algorithm = htons(NX_MP_ALG_ITER_HASH);
+ mp->algorithm = NX_MP_ALG_ITER_HASH;
} else {
ovs_fatal(0, "%s: unknown algorithm `%s'", s_, algorithm);
}
ovs_fatal(0, "%s: n_links %d is not in valid range 1 to 65536",
s_, n_links);
}
- mp->max_link = htons(n_links - 1);
- mp->arg = htonl(atoi(arg));
+ mp->max_link = n_links - 1;
+ mp->arg = atoi(arg);
- mf_parse_subfield(&dst, dst_s);
- if (dst.n_bits < 16 && n_links > (1u << dst.n_bits)) {
+ mf_parse_subfield(&mp->dst, dst);
+ if (mp->dst.n_bits < 16 && n_links > (1u << mp->dst.n_bits)) {
ovs_fatal(0, "%s: %d-bit destination field has %u possible values, "
"less than specified n_links %d",
- s_, dst.n_bits, 1u << dst.n_bits, n_links);
+ s_, mp->dst.n_bits, 1u << mp->dst.n_bits, n_links);
}
- mp->ofs_nbits = nxm_encode_ofs_nbits(dst.ofs, dst.n_bits);
- mp->dst = htonl(dst.field->nxm_header);
free(s);
}
+/* Appends a description of 'mp' to 's', in the format that ovs-ofctl(8)
+ * describes. */
void
-multipath_format(const struct nx_action_multipath *mp, struct ds *s)
+multipath_format(const struct ofpact_multipath *mp, struct ds *s)
{
const char *fields, *algorithm;
- uint16_t mp_fields = ntohs(mp->fields);
- uint16_t mp_algorithm = ntohs(mp->algorithm);
-
- struct mf_subfield dst;
-
- fields = flow_hash_fields_to_str(mp_fields);
+ fields = flow_hash_fields_to_str(mp->fields);
- switch ((enum nx_mp_algorithm) mp_algorithm) {
+ switch (mp->algorithm) {
case NX_MP_ALG_MODULO_N:
algorithm = "modulo_n";
break;
}
ds_put_format(s, "multipath(%s,%"PRIu16",%s,%d,%"PRIu16",",
- fields, ntohs(mp->basis), algorithm, ntohs(mp->max_link) + 1,
- ntohl(mp->arg));
- nxm_decode(&dst, mp->dst, mp->ofs_nbits);
- mf_format_subfield(&dst, s);
+ fields, mp->basis, algorithm, mp->max_link + 1,
+ mp->arg);
+ mf_format_subfield(&mp->dst, s);
ds_put_char(s, ')');
}
/*
- * Copyright (c) 2010, 2011 Nicira, Inc.
+ * Copyright (c) 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.
struct ds;
struct flow;
struct nx_action_multipath;
-struct nx_action_reg_move;
+struct ofpact_multipath;
+struct ofpbuf;
/* NXAST_MULTIPATH helper functions.
*
* See include/openflow/nicira-ext.h for NXAST_MULTIPATH specification.
*/
-enum ofperr multipath_check(const struct nx_action_multipath *,
+enum ofperr multipath_from_openflow(const struct nx_action_multipath *,
+ struct ofpact_multipath *);
+enum ofperr multipath_check(const struct ofpact_multipath *,
const struct flow *);
-void multipath_execute(const struct nx_action_multipath *, struct flow *);
+void multipath_to_nxast(const struct ofpact_multipath *,
+ struct ofpbuf *openflow);
-void multipath_parse(struct nx_action_multipath *, const char *);
-void multipath_format(const struct nx_action_multipath *, struct ds *);
+void multipath_execute(const struct ofpact_multipath *, struct flow *);
+
+void multipath_parse(struct ofpact_multipath *, const char *);
+void multipath_format(const struct ofpact_multipath *, struct ds *);
#endif /* multipath.h */
#include "classifier.h"
#include "dynamic-string.h"
#include "meta-flow.h"
+#include "ofp-actions.h"
#include "ofp-errors.h"
#include "ofp-util.h"
#include "ofpbuf.h"
}
\f
void
-nxm_parse_reg_move(struct nx_action_reg_move *move, const char *s)
+nxm_parse_reg_move(struct ofpact_reg_move *move, const char *s)
{
const char *full_s = s;
- struct mf_subfield src, dst;
- s = mf_parse_subfield(&src, s);
+ s = mf_parse_subfield(&move->src, s);
if (strncmp(s, "->", 2)) {
ovs_fatal(0, "%s: missing `->' following source", full_s);
}
s += 2;
- s = mf_parse_subfield(&dst, s);
+ s = mf_parse_subfield(&move->dst, s);
if (*s != '\0') {
ovs_fatal(0, "%s: trailing garbage following destination", full_s);
}
- if (src.n_bits != dst.n_bits) {
+ if (move->src.n_bits != move->dst.n_bits) {
ovs_fatal(0, "%s: source field is %d bits wide but destination is "
- "%d bits wide", full_s, src.n_bits, dst.n_bits);
+ "%d bits wide", full_s,
+ move->src.n_bits, move->dst.n_bits);
}
-
- ofputil_init_NXAST_REG_MOVE(move);
- move->n_bits = htons(src.n_bits);
- move->src_ofs = htons(src.ofs);
- move->dst_ofs = htons(dst.ofs);
- move->src = htonl(src.field->nxm_header);
- move->dst = htonl(dst.field->nxm_header);
}
void
-nxm_parse_reg_load(struct nx_action_reg_load *load, const char *s)
+nxm_parse_reg_load(struct ofpact_reg_load *load, const char *s)
{
const char *full_s = s;
- struct mf_subfield dst;
- uint64_t value;
- value = strtoull(s, (char **) &s, 0);
+ load->value = strtoull(s, (char **) &s, 0);
if (strncmp(s, "->", 2)) {
ovs_fatal(0, "%s: missing `->' following value", full_s);
}
s += 2;
- s = mf_parse_subfield(&dst, s);
+ s = mf_parse_subfield(&load->dst, s);
if (*s != '\0') {
ovs_fatal(0, "%s: trailing garbage following destination", full_s);
}
- if (dst.n_bits < 64 && (value >> dst.n_bits) != 0) {
- ovs_fatal(0, "%s: value %"PRIu64" does not fit into %u bits",
- full_s, value, dst.n_bits);
+ if (load->dst.n_bits < 64 && (load->value >> load->dst.n_bits) != 0) {
+ ovs_fatal(0, "%s: value %"PRIu64" does not fit into %d bits",
+ full_s, load->value, load->dst.n_bits);
}
-
- ofputil_init_NXAST_REG_LOAD(load);
- load->ofs_nbits = nxm_encode_ofs_nbits(dst.ofs, dst.n_bits);
- load->dst = htonl(dst.field->nxm_header);
- load->value = htonll(value);
}
\f
/* nxm_format_reg_move(), nxm_format_reg_load(). */
void
-nxm_format_reg_move(const struct nx_action_reg_move *move, struct ds *s)
+nxm_format_reg_move(const struct ofpact_reg_move *move, struct ds *s)
{
- struct mf_subfield src, dst;
-
- nxm_decode_discrete(&src, move->src, move->src_ofs, move->n_bits);
- nxm_decode_discrete(&dst, move->dst, move->dst_ofs, move->n_bits);
-
ds_put_format(s, "move:");
- mf_format_subfield(&src, s);
+ mf_format_subfield(&move->src, s);
ds_put_cstr(s, "->");
- mf_format_subfield(&dst, s);
+ mf_format_subfield(&move->dst, s);
}
void
-nxm_format_reg_load(const struct nx_action_reg_load *load, struct ds *s)
+nxm_format_reg_load(const struct ofpact_reg_load *load, struct ds *s)
+{
+ ds_put_format(s, "load:%#"PRIx64"->", load->value);
+ mf_format_subfield(&load->dst, s);
+}
+\f
+enum ofperr
+nxm_reg_move_from_openflow(const struct nx_action_reg_move *narm,
+ struct ofpbuf *ofpacts)
{
- struct mf_subfield dst;
+ struct ofpact_reg_move *move;
- ds_put_format(s, "load:%#"PRIx64"->", ntohll(load->value));
+ move = ofpact_put_REG_MOVE(ofpacts);
+ move->src.field = mf_from_nxm_header(ntohl(narm->src));
+ move->src.ofs = ntohs(narm->src_ofs);
+ move->src.n_bits = ntohs(narm->n_bits);
+ move->dst.field = mf_from_nxm_header(ntohl(narm->dst));
+ move->dst.ofs = ntohs(narm->dst_ofs);
+ move->dst.n_bits = ntohs(narm->n_bits);
- nxm_decode(&dst, load->dst, load->ofs_nbits);
- mf_format_subfield(&dst, s);
+ return nxm_reg_move_check(move, NULL);
}
-\f
-/* nxm_check_reg_move(), nxm_check_reg_load(). */
enum ofperr
-nxm_check_reg_move(const struct nx_action_reg_move *action,
- const struct flow *flow)
+nxm_reg_load_from_openflow(const struct nx_action_reg_load *narl,
+ struct ofpbuf *ofpacts)
{
- struct mf_subfield src;
- struct mf_subfield dst;
- int error;
+ struct ofpact_reg_load *load;
- nxm_decode_discrete(&src, action->src, action->src_ofs, action->n_bits);
- error = mf_check_src(&src, flow);
- if (error) {
- return error;
+ load = ofpact_put_REG_LOAD(ofpacts);
+ load->dst.field = mf_from_nxm_header(ntohl(narl->dst));
+ load->dst.ofs = nxm_decode_ofs(narl->ofs_nbits);
+ load->dst.n_bits = nxm_decode_n_bits(narl->ofs_nbits);
+ load->value = ntohll(narl->value);
+
+ /* Reject 'narl' if a bit numbered 'n_bits' or higher is set to 1 in
+ * narl->value. */
+ if (load->dst.n_bits < 64 && load->value >> load->dst.n_bits) {
+ return OFPERR_OFPBAC_BAD_ARGUMENT;
}
- nxm_decode_discrete(&dst, action->dst, action->dst_ofs, action->n_bits);
- return mf_check_dst(&dst, flow);
+ return nxm_reg_load_check(load, NULL);
}
-
+\f
enum ofperr
-nxm_check_reg_load(const struct nx_action_reg_load *action,
- const struct flow *flow)
+nxm_reg_move_check(const struct ofpact_reg_move *move, const struct flow *flow)
{
- struct mf_subfield dst;
enum ofperr error;
- nxm_decode(&dst, action->dst, action->ofs_nbits);
- error = mf_check_dst(&dst, flow);
+ error = mf_check_src(&move->src, flow);
if (error) {
return error;
}
- /* Reject 'action' if a bit numbered 'n_bits' or higher is set to 1 in
- * action->value. */
- if (dst.n_bits < 64 && ntohll(action->value) >> dst.n_bits) {
- return OFPERR_OFPBAC_BAD_ARGUMENT;
- }
+ return mf_check_dst(&move->dst, NULL);
+}
- return 0;
+enum ofperr
+nxm_reg_load_check(const struct ofpact_reg_load *load, const struct flow *flow)
+{
+ return mf_check_dst(&load->dst, flow);
}
\f
-/* nxm_execute_reg_move(), nxm_execute_reg_load(). */
-
void
-nxm_execute_reg_move(const struct nx_action_reg_move *action,
- struct flow *flow)
+nxm_reg_move_to_nxast(const struct ofpact_reg_move *move,
+ struct ofpbuf *openflow)
{
- struct mf_subfield src, dst;
- union mf_value src_value;
- union mf_value dst_value;
+ struct nx_action_reg_move *narm;
- nxm_decode_discrete(&src, action->src, action->src_ofs, action->n_bits);
- nxm_decode_discrete(&dst, action->dst, action->dst_ofs, action->n_bits);
+ narm = ofputil_put_NXAST_REG_MOVE(openflow);
+ narm->n_bits = htons(move->dst.n_bits);
+ narm->src_ofs = htons(move->src.ofs);
+ narm->dst_ofs = htons(move->dst.ofs);
+ narm->src = htonl(move->src.field->nxm_header);
+ narm->dst = htonl(move->dst.field->nxm_header);
+}
- mf_get_value(dst.field, flow, &dst_value);
- mf_get_value(src.field, flow, &src_value);
- bitwise_copy(&src_value, src.field->n_bytes, src.ofs,
- &dst_value, dst.field->n_bytes, dst.ofs,
- src.n_bits);
- mf_set_flow_value(dst.field, &dst_value, flow);
+void
+nxm_reg_load_to_nxast(const struct ofpact_reg_load *load,
+ struct ofpbuf *openflow)
+{
+ struct nx_action_reg_load *narl;
+
+ narl = ofputil_put_NXAST_REG_LOAD(openflow);
+ narl->ofs_nbits = nxm_encode_ofs_nbits(load->dst.ofs, load->dst.n_bits);
+ narl->dst = htonl(load->dst.field->nxm_header);
+ narl->value = htonll(load->value);
}
+\f
+/* nxm_execute_reg_move(), nxm_execute_reg_load(). */
void
-nxm_execute_reg_load(const struct nx_action_reg_load *action,
+nxm_execute_reg_move(const struct ofpact_reg_move *move,
struct flow *flow)
{
- struct mf_subfield dst;
+ union mf_value src_value;
+ union mf_value dst_value;
- nxm_decode(&dst, action->dst, action->ofs_nbits);
- mf_set_subfield_value(&dst, ntohll(action->value), flow);
+ mf_get_value(move->dst.field, flow, &dst_value);
+ mf_get_value(move->src.field, flow, &src_value);
+ bitwise_copy(&src_value, move->src.field->n_bytes, move->src.ofs,
+ &dst_value, move->dst.field->n_bytes, move->dst.ofs,
+ move->src.n_bits);
+ mf_set_flow_value(move->dst.field, &dst_value, flow);
}
-/* Initializes 'sf->field' with the field corresponding to the given NXM
- * 'header' and 'sf->ofs' and 'sf->n_bits' decoded from 'ofs_nbits' with
- * nxm_decode_ofs() and nxm_decode_n_bits(), respectively.
- *
- * Afterward, 'sf' might be invalid in a few different ways:
- *
- * - 'sf->field' will be NULL if 'header' is unknown.
- *
- * - 'sf->ofs' and 'sf->n_bits' might exceed the width of sf->field.
- *
- * The caller should call mf_check_src() or mf_check_dst() to check for these
- * problems. */
void
-nxm_decode(struct mf_subfield *sf, ovs_be32 header, ovs_be16 ofs_nbits)
+nxm_execute_reg_load(const struct ofpact_reg_load *load, struct flow *flow)
{
- sf->field = mf_from_nxm_header(ntohl(header));
- sf->ofs = nxm_decode_ofs(ofs_nbits);
- sf->n_bits = nxm_decode_n_bits(ofs_nbits);
+ nxm_reg_load(&load->dst, load->value, flow);
}
-/* Initializes 'sf->field' with the field corresponding to the given NXM
- * 'header' and 'sf->ofs' and 'sf->n_bits' from 'ofs' and 'n_bits',
- * respectively.
- *
- * Afterward, 'sf' might be invalid in a few different ways:
- *
- * - 'sf->field' will be NULL if 'header' is unknown.
- *
- * - 'sf->ofs' and 'sf->n_bits' might exceed the width of sf->field.
- *
- * The caller should call mf_check_src() or mf_check_dst() to check for these
- * problems. */
void
-nxm_decode_discrete(struct mf_subfield *sf, ovs_be32 header,
- ovs_be16 ofs, ovs_be16 n_bits)
+nxm_reg_load(const struct mf_subfield *dst, uint64_t src_data,
+ struct flow *flow)
{
- sf->field = mf_from_nxm_header(ntohl(header));
- sf->ofs = ntohs(ofs);
- sf->n_bits = ntohs(n_bits);
+ union mf_value dst_value;
+ union mf_value src_value;
+
+ mf_get_value(dst->field, flow, &dst_value);
+ src_value.be64 = htonll(src_data);
+ bitwise_copy(&src_value, sizeof src_value.be64, 0,
+ &dst_value, dst->field->n_bytes, dst->ofs,
+ dst->n_bits);
+ mf_set_flow_value(dst->field, &dst_value, flow);
}
#include <sys/types.h>
#include <netinet/in.h>
#include "flow.h"
+#include "ofp-errors.h"
#include "openvswitch/types.h"
#include "ofp-errors.h"
struct ds;
struct flow;
struct mf_subfield;
+struct ofpact_reg_move;
+struct ofpact_reg_load;
struct ofpbuf;
struct nx_action_reg_load;
struct nx_action_reg_move;
char *nx_match_to_string(const uint8_t *, unsigned int match_len);
int nx_match_from_string(const char *, struct ofpbuf *);
-void nxm_parse_reg_move(struct nx_action_reg_move *, const char *);
-void nxm_parse_reg_load(struct nx_action_reg_load *, const char *);
+void nxm_parse_reg_move(struct ofpact_reg_move *, const char *);
+void nxm_parse_reg_load(struct ofpact_reg_load *, const char *);
+
+void nxm_format_reg_move(const struct ofpact_reg_move *, struct ds *);
+void nxm_format_reg_load(const struct ofpact_reg_load *, struct ds *);
-void nxm_format_reg_move(const struct nx_action_reg_move *, struct ds *);
-void nxm_format_reg_load(const struct nx_action_reg_load *, struct ds *);
+enum ofperr nxm_reg_move_from_openflow(const struct nx_action_reg_move *,
+ struct ofpbuf *ofpacts);
+enum ofperr nxm_reg_load_from_openflow(const struct nx_action_reg_load *,
+ struct ofpbuf *ofpacts);
-enum ofperr nxm_check_reg_move(const struct nx_action_reg_move *,
+enum ofperr nxm_reg_move_check(const struct ofpact_reg_move *,
const struct flow *);
-enum ofperr nxm_check_reg_load(const struct nx_action_reg_load *,
+enum ofperr nxm_reg_load_check(const struct ofpact_reg_load *,
const struct flow *);
-void nxm_execute_reg_move(const struct nx_action_reg_move *, struct flow *);
-void nxm_execute_reg_load(const struct nx_action_reg_load *, struct flow *);
+void nxm_reg_move_to_nxast(const struct ofpact_reg_move *,
+ struct ofpbuf *openflow);
+void nxm_reg_load_to_nxast(const struct ofpact_reg_load *,
+ struct ofpbuf *openflow);
+
+void nxm_execute_reg_move(const struct ofpact_reg_move *, struct flow *);
+void nxm_execute_reg_load(const struct ofpact_reg_load *, struct flow *);
+void nxm_reg_load(const struct mf_subfield *, uint64_t src_data,
+ struct flow *);
int nxm_field_bytes(uint32_t header);
int nxm_field_bits(uint32_t header);
{
return (ntohs(ofs_nbits) & 0x3f) + 1;
}
-
-void nxm_decode(struct mf_subfield *, ovs_be32 header, ovs_be16 ofs_nbits);
-void nxm_decode_discrete(struct mf_subfield *, ovs_be32 header,
- ovs_be16 ofs, ovs_be16 n_bits);
\f
BUILD_ASSERT_DECL(FLOW_WC_SEQ == 12);
/* Upper bound on the length of an nx_match. The longest nx_match (an
--- /dev/null
+/*
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira Networks.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <config.h>
+#include "ofp-actions.h"
+#include "autopath.h"
+#include "bundle.h"
+#include "byte-order.h"
+#include "compiler.h"
+#include "dynamic-string.h"
+#include "learn.h"
+#include "meta-flow.h"
+#include "multipath.h"
+#include "nx-match.h"
+#include "ofp-util.h"
+#include "ofpbuf.h"
+#include "vlog.h"
+
+VLOG_DEFINE_THIS_MODULE(ofp_actions);
+
+static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
+\f
+/* Converting OpenFlow 1.0 to ofpacts. */
+
+static enum ofperr
+output_from_openflow10(const struct ofp_action_output *oao,
+ struct ofpbuf *out)
+{
+ struct ofpact_output *output;
+
+ output = ofpact_put_OUTPUT(out);
+ output->port = ntohs(oao->port);
+ output->max_len = ntohs(oao->max_len);
+
+ return ofputil_check_output_port(output->port, OFPP_MAX);
+}
+
+static enum ofperr
+enqueue_from_openflow10(const struct ofp_action_enqueue *oae,
+ struct ofpbuf *out)
+{
+ struct ofpact_enqueue *enqueue;
+
+ enqueue = ofpact_put_ENQUEUE(out);
+ enqueue->port = ntohs(oae->port);
+ enqueue->queue = ntohl(oae->queue_id);
+ if (enqueue->port >= OFPP_MAX && enqueue->port != OFPP_IN_PORT
+ && enqueue->port != OFPP_LOCAL) {
+ return OFPERR_OFPBAC_BAD_OUT_PORT;
+ }
+ return 0;
+}
+
+static void
+resubmit_from_openflow(const struct nx_action_resubmit *nar,
+ struct ofpbuf *out)
+{
+ struct ofpact_resubmit *resubmit;
+
+ resubmit = ofpact_put_RESUBMIT(out);
+ resubmit->ofpact.compat = OFPUTIL_NXAST_RESUBMIT;
+ resubmit->in_port = ntohs(nar->in_port);
+ resubmit->table_id = 0xff;
+}
+
+static enum ofperr
+resubmit_table_from_openflow(const struct nx_action_resubmit *nar,
+ struct ofpbuf *out)
+{
+ struct ofpact_resubmit *resubmit;
+
+ if (nar->pad[0] || nar->pad[1] || nar->pad[2]) {
+ return OFPERR_OFPBAC_BAD_ARGUMENT;
+ }
+
+ resubmit = ofpact_put_RESUBMIT(out);
+ resubmit->ofpact.compat = OFPUTIL_NXAST_RESUBMIT_TABLE;
+ resubmit->in_port = ntohs(nar->in_port);
+ resubmit->table_id = nar->table;
+ return 0;
+}
+
+static enum ofperr
+output_reg_from_openflow(const struct nx_action_output_reg *naor,
+ struct ofpbuf *out)
+{
+ struct ofpact_output_reg *output_reg;
+
+ if (!is_all_zeros(naor->zero, sizeof naor->zero)) {
+ return OFPERR_OFPBAC_BAD_ARGUMENT;
+ }
+
+ output_reg = ofpact_put_OUTPUT_REG(out);
+ output_reg->src.field = mf_from_nxm_header(ntohl(naor->src));
+ output_reg->src.ofs = nxm_decode_ofs(naor->ofs_nbits);
+ output_reg->src.n_bits = nxm_decode_n_bits(naor->ofs_nbits);
+ output_reg->max_len = ntohs(naor->max_len);
+
+ return mf_check_src(&output_reg->src, NULL);
+}
+
+static void
+fin_timeout_from_openflow(const struct nx_action_fin_timeout *naft,
+ struct ofpbuf *out)
+{
+ struct ofpact_fin_timeout *oft;
+
+ oft = ofpact_put_FIN_TIMEOUT(out);
+ oft->fin_idle_timeout = ntohs(naft->fin_idle_timeout);
+ oft->fin_hard_timeout = ntohs(naft->fin_hard_timeout);
+}
+
+static void
+controller_from_openflow(const struct nx_action_controller *nac,
+ struct ofpbuf *out)
+{
+ struct ofpact_controller *oc;
+
+ oc = ofpact_put_CONTROLLER(out);
+ oc->max_len = ntohs(nac->max_len);
+ oc->controller_id = ntohs(nac->controller_id);
+ oc->reason = nac->reason;
+}
+
+static void
+note_from_openflow(const struct nx_action_note *nan, struct ofpbuf *out)
+{
+ struct ofpact_note *note;
+ unsigned int length;
+
+ length = ntohs(nan->len) - offsetof(struct nx_action_note, note);
+ note = ofpact_put(out, OFPACT_NOTE,
+ offsetof(struct ofpact_note, data) + length);
+ note->length = length;
+ memcpy(note->data, nan->note, length);
+}
+
+static enum ofperr
+decode_nxast_action(const union ofp_action *a, enum ofputil_action_code *code)
+{
+ const struct nx_action_header *nah = (const struct nx_action_header *) a;
+ uint16_t len = ntohs(a->header.len);
+
+ if (len < sizeof(struct nx_action_header)) {
+ return OFPERR_OFPBAC_BAD_LEN;
+ } else if (a->vendor.vendor != CONSTANT_HTONL(NX_VENDOR_ID)) {
+ return OFPERR_OFPBAC_BAD_VENDOR;
+ }
+
+ switch (nah->subtype) {
+#define NXAST_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME) \
+ case CONSTANT_HTONS(ENUM): \
+ 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"
+
+ case CONSTANT_HTONS(NXAST_SNAT__OBSOLETE):
+ case CONSTANT_HTONS(NXAST_DROP_SPOOFED_ARP__OBSOLETE):
+ default:
+ return OFPERR_OFPBAC_BAD_TYPE;
+ }
+}
+
+/* Parses 'a' to determine its type. On success stores the correct type into
+ * '*code' and returns 0. On failure returns an OFPERR_* error code and
+ * '*code' is indeterminate.
+ *
+ * The caller must have already verified that 'a''s length is potentially
+ * correct (that is, a->header.len is nonzero and a multiple of sizeof(union
+ * ofp_action) and no longer than the amount of space allocated to 'a').
+ *
+ * This function verifies that 'a''s length is correct for the type of action
+ * that it represents. */
+static enum ofperr
+decode_openflow10_action(const union ofp_action *a,
+ enum ofputil_action_code *code)
+{
+ switch (a->type) {
+ case CONSTANT_HTONS(OFPAT10_VENDOR):
+ return decode_nxast_action(a, code);
+
+#define OFPAT10_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;
+#include "ofp-util.def"
+
+ default:
+ return OFPERR_OFPBAC_BAD_TYPE;
+ }
+}
+
+static enum ofperr
+ofpact_from_openflow10__(const union ofp_action *a, struct ofpbuf *out)
+{
+ const struct nx_action_resubmit *nar;
+ const struct nx_action_set_tunnel *nast;
+ const struct nx_action_set_queue *nasq;
+ const struct nx_action_note *nan;
+ const struct nx_action_set_tunnel64 *nast64;
+ struct ofpact_tunnel *tunnel;
+ enum ofputil_action_code code;
+ enum ofperr error;
+
+ error = decode_openflow10_action(a, &code);
+ if (error) {
+ return error;
+ }
+
+ switch (code) {
+ case OFPUTIL_ACTION_INVALID:
+ NOT_REACHED();
+
+ case OFPUTIL_OFPAT10_OUTPUT:
+ return output_from_openflow10((const struct ofp_action_output *) a,
+ out);
+
+ case OFPUTIL_OFPAT10_SET_VLAN_VID:
+ if (a->vlan_vid.vlan_vid & ~htons(0xfff)) {
+ return OFPERR_OFPBAC_BAD_ARGUMENT;
+ }
+ ofpact_put_SET_VLAN_VID(out)->vlan_vid = ntohs(a->vlan_vid.vlan_vid);
+ break;
+
+ case OFPUTIL_OFPAT10_SET_VLAN_PCP:
+ if (a->vlan_pcp.vlan_pcp & ~7) {
+ return OFPERR_OFPBAC_BAD_ARGUMENT;
+ }
+ ofpact_put_SET_VLAN_PCP(out)->vlan_pcp = a->vlan_pcp.vlan_pcp;
+ break;
+
+ case OFPUTIL_OFPAT10_STRIP_VLAN:
+ ofpact_put_STRIP_VLAN(out);
+ break;
+
+ case OFPUTIL_OFPAT10_SET_DL_SRC:
+ memcpy(ofpact_put_SET_ETH_SRC(out)->mac,
+ ((const struct ofp_action_dl_addr *) a)->dl_addr, ETH_ADDR_LEN);
+ break;
+
+ case OFPUTIL_OFPAT10_SET_DL_DST:
+ memcpy(ofpact_put_SET_ETH_DST(out)->mac,
+ ((const struct ofp_action_dl_addr *) a)->dl_addr, ETH_ADDR_LEN);
+ break;
+
+ case OFPUTIL_OFPAT10_SET_NW_SRC:
+ ofpact_put_SET_IPV4_SRC(out)->ipv4 = a->nw_addr.nw_addr;
+ break;
+
+ case OFPUTIL_OFPAT10_SET_NW_DST:
+ ofpact_put_SET_IPV4_DST(out)->ipv4 = a->nw_addr.nw_addr;
+ break;
+
+ case OFPUTIL_OFPAT10_SET_NW_TOS:
+ if (a->nw_tos.nw_tos & ~IP_DSCP_MASK) {
+ return OFPERR_OFPBAC_BAD_ARGUMENT;
+ }
+ ofpact_put_SET_IPV4_DSCP(out)->dscp = a->nw_tos.nw_tos;
+ break;
+
+ case OFPUTIL_OFPAT10_SET_TP_SRC:
+ ofpact_put_SET_L4_SRC_PORT(out)->port = ntohs(a->tp_port.tp_port);
+ break;
+
+ case OFPUTIL_OFPAT10_SET_TP_DST:
+ ofpact_put_SET_L4_DST_PORT(out)->port = ntohs(a->tp_port.tp_port);
+
+ break;
+
+ case OFPUTIL_OFPAT10_ENQUEUE:
+ error = enqueue_from_openflow10((const struct ofp_action_enqueue *) a,
+ out);
+ break;
+
+ case OFPUTIL_NXAST_RESUBMIT:
+ resubmit_from_openflow((const struct nx_action_resubmit *) a, out);
+ break;
+
+ case OFPUTIL_NXAST_SET_TUNNEL:
+ nast = (const struct nx_action_set_tunnel *) a;
+ tunnel = ofpact_put_SET_TUNNEL(out);
+ tunnel->ofpact.compat = code;
+ tunnel->tun_id = ntohl(nast->tun_id);
+ 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_POP_QUEUE:
+ ofpact_put_POP_QUEUE(out);
+ break;
+
+ case OFPUTIL_NXAST_REG_MOVE:
+ error = nxm_reg_move_from_openflow(
+ (const struct nx_action_reg_move *) a, out);
+ break;
+
+ case OFPUTIL_NXAST_REG_LOAD:
+ error = nxm_reg_load_from_openflow(
+ (const struct nx_action_reg_load *) a, out);
+ break;
+
+ case OFPUTIL_NXAST_NOTE:
+ nan = (const struct nx_action_note *) a;
+ note_from_openflow(nan, out);
+ break;
+
+ case OFPUTIL_NXAST_SET_TUNNEL64:
+ nast64 = (const struct nx_action_set_tunnel64 *) a;
+ tunnel = ofpact_put_SET_TUNNEL(out);
+ tunnel->ofpact.compat = code;
+ tunnel->tun_id = ntohll(nast64->tun_id);
+ break;
+
+ case OFPUTIL_NXAST_MULTIPATH:
+ error = multipath_from_openflow((const struct nx_action_multipath *) a,
+ ofpact_put_MULTIPATH(out));
+ break;
+
+ case OFPUTIL_NXAST_AUTOPATH:
+ error = autopath_from_openflow((const struct nx_action_autopath *) a,
+ ofpact_put_AUTOPATH(out));
+ break;
+
+ case OFPUTIL_NXAST_BUNDLE:
+ case OFPUTIL_NXAST_BUNDLE_LOAD:
+ error = bundle_from_openflow((const struct nx_action_bundle *) a, out);
+ break;
+
+ case OFPUTIL_NXAST_OUTPUT_REG:
+ error = output_reg_from_openflow(
+ (const struct nx_action_output_reg *) a, out);
+ break;
+
+ case OFPUTIL_NXAST_RESUBMIT_TABLE:
+ nar = (const struct nx_action_resubmit *) a;
+ error = resubmit_table_from_openflow(nar, out);
+ break;
+
+ case OFPUTIL_NXAST_LEARN:
+ error = learn_from_openflow((const struct nx_action_learn *) a, out);
+ break;
+
+ case OFPUTIL_NXAST_EXIT:
+ ofpact_put_EXIT(out);
+ break;
+
+ case OFPUTIL_NXAST_DEC_TTL:
+ ofpact_put_DEC_TTL(out);
+ break;
+
+ case OFPUTIL_NXAST_FIN_TIMEOUT:
+ fin_timeout_from_openflow(
+ (const struct nx_action_fin_timeout *) a, out);
+ break;
+
+ case OFPUTIL_NXAST_CONTROLLER:
+ controller_from_openflow((const struct nx_action_controller *) a, out);
+ break;
+ }
+
+ return error;
+}
+
+static inline union ofp_action *
+action_next(const union ofp_action *a)
+{
+ return ((union ofp_action *) (void *)
+ ((uint8_t *) a + ntohs(a->header.len)));
+}
+
+static inline bool
+action_is_valid(const union ofp_action *a, size_t n_actions)
+{
+ uint16_t len = ntohs(a->header.len);
+ return (!(len % OFP_ACTION_ALIGN)
+ && len >= sizeof *a
+ && len / sizeof *a <= n_actions);
+}
+
+/* This macro is careful to check for actions with bad lengths. */
+#define ACTION_FOR_EACH(ITER, LEFT, ACTIONS, N_ACTIONS) \
+ for ((ITER) = (ACTIONS), (LEFT) = (N_ACTIONS); \
+ (LEFT) > 0 && action_is_valid(ITER, LEFT); \
+ ((LEFT) -= ntohs((ITER)->header.len) / sizeof(union ofp_action), \
+ (ITER) = action_next(ITER)))
+
+static enum ofperr
+ofpact_from_openflow10(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_openflow10__(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;
+ }
+
+ ofpact_pad(out);
+ return 0;
+}
+
+/* Attempts to convert 'actions_len' bytes of OpenFlow actions from the front
+ * of 'openflow' into ofpacts. On success, replaces any existing content 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(). */
+enum ofperr
+ofpacts_pull_openflow(struct ofpbuf *openflow, unsigned int actions_len,
+ struct ofpbuf *ofpacts)
+{
+ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
+ const union ofp_action *actions;
+ enum ofperr error;
+
+ ofpbuf_clear(ofpacts);
+
+ if (actions_len % OFP_ACTION_ALIGN != 0) {
+ VLOG_WARN_RL(&rl, "OpenFlow message actions length %u is not a "
+ "multiple of %d", actions_len, OFP_ACTION_ALIGN);
+ return OFPERR_OFPBRC_BAD_LEN;
+ }
+
+ actions = ofpbuf_try_pull(openflow, actions_len);
+ if (actions == NULL) {
+ VLOG_WARN_RL(&rl, "OpenFlow message actions length %u exceeds "
+ "remaining message length (%zu)",
+ actions_len, openflow->size);
+ return OFPERR_OFPBRC_BAD_LEN;
+ }
+
+ error = ofpact_from_openflow10(actions, actions_len / OFP_ACTION_ALIGN,
+ ofpacts);
+ if (error) {
+ ofpbuf_clear(ofpacts);
+ }
+ return 0;
+}
+\f
+static enum ofperr
+ofpact_check__(const struct ofpact *a, const struct flow *flow, int max_ports)
+{
+ const struct ofpact_enqueue *enqueue;
+
+ switch (a->type) {
+ case OFPACT_OUTPUT:
+ return ofputil_check_output_port(ofpact_get_OUTPUT(a)->port,
+ max_ports);
+
+ case OFPACT_CONTROLLER:
+ return 0;
+
+ case OFPACT_ENQUEUE:
+ enqueue = ofpact_get_ENQUEUE(a);
+ if (enqueue->port >= max_ports && enqueue->port != OFPP_IN_PORT
+ && enqueue->port != OFPP_LOCAL) {
+ return OFPERR_OFPBAC_BAD_OUT_PORT;
+ }
+ return 0;
+
+ case OFPACT_OUTPUT_REG:
+ return mf_check_src(&ofpact_get_OUTPUT_REG(a)->src, flow);
+
+ case OFPACT_BUNDLE:
+ return bundle_check(ofpact_get_BUNDLE(a), max_ports, flow);
+
+ case OFPACT_SET_VLAN_VID:
+ case OFPACT_SET_VLAN_PCP:
+ case OFPACT_STRIP_VLAN:
+ case OFPACT_SET_ETH_SRC:
+ case OFPACT_SET_ETH_DST:
+ case OFPACT_SET_IPV4_SRC:
+ case OFPACT_SET_IPV4_DST:
+ case OFPACT_SET_IPV4_DSCP:
+ case OFPACT_SET_L4_SRC_PORT:
+ case OFPACT_SET_L4_DST_PORT:
+ return 0;
+
+ case OFPACT_REG_MOVE:
+ return nxm_reg_move_check(ofpact_get_REG_MOVE(a), flow);
+
+ case OFPACT_REG_LOAD:
+ return nxm_reg_load_check(ofpact_get_REG_LOAD(a), flow);
+
+ case OFPACT_DEC_TTL:
+ case OFPACT_SET_TUNNEL:
+ case OFPACT_SET_QUEUE:
+ case OFPACT_POP_QUEUE:
+ case OFPACT_FIN_TIMEOUT:
+ case OFPACT_RESUBMIT:
+ return 0;
+
+ case OFPACT_LEARN:
+ return learn_check(ofpact_get_LEARN(a), flow);
+
+ case OFPACT_MULTIPATH:
+ return multipath_check(ofpact_get_MULTIPATH(a), flow);
+
+ case OFPACT_AUTOPATH:
+ return autopath_check(ofpact_get_AUTOPATH(a), flow);
+
+ case OFPACT_NOTE:
+ case OFPACT_EXIT:
+ return 0;
+
+ default:
+ NOT_REACHED();
+ }
+}
+
+/* Checks that the 'ofpacts_len' bytes of actions in 'ofpacts' are
+ * appropriate for a packet with the prerequisites satisfied by 'flow' in a
+ * switch with no more than 'max_ports' ports. */
+enum ofperr
+ofpacts_check(const struct ofpact ofpacts[], size_t ofpacts_len,
+ const struct flow *flow, int max_ports)
+{
+ const struct ofpact *a;
+
+ OFPACT_FOR_EACH (a, ofpacts, ofpacts_len) {
+ enum ofperr error = ofpact_check__(a, flow, max_ports);
+ if (error) {
+ return error;
+ }
+ }
+
+ return 0;
+}
+\f
+/* Converting ofpacts to Nicira OpenFlow extensions. */
+
+static void
+ofpact_output_reg_to_nxast(const struct ofpact_output_reg *output_reg,
+ struct ofpbuf *out)
+{
+ struct nx_action_output_reg *naor = ofputil_put_NXAST_OUTPUT_REG(out);
+
+ naor->ofs_nbits = nxm_encode_ofs_nbits(output_reg->src.ofs,
+ output_reg->src.n_bits);
+ naor->src = htonl(output_reg->src.field->nxm_header);
+ naor->max_len = htons(output_reg->max_len);
+}
+
+static void
+ofpact_resubmit_to_nxast(const struct ofpact_resubmit *resubmit,
+ struct ofpbuf *out)
+{
+ struct nx_action_resubmit *nar;
+
+ if (resubmit->table_id == 0xff
+ && resubmit->ofpact.compat != OFPUTIL_NXAST_RESUBMIT_TABLE) {
+ nar = ofputil_put_NXAST_RESUBMIT(out);
+ } else {
+ nar = ofputil_put_NXAST_RESUBMIT_TABLE(out);
+ nar->table = resubmit->table_id;
+ }
+ nar->in_port = htons(resubmit->in_port);
+}
+
+static void
+ofpact_set_tunnel_to_nxast(const struct ofpact_tunnel *tunnel,
+ struct ofpbuf *out)
+{
+ uint64_t tun_id = tunnel->tun_id;
+
+ if (tun_id <= UINT32_MAX
+ && tunnel->ofpact.compat != OFPUTIL_NXAST_SET_TUNNEL64) {
+ ofputil_put_NXAST_SET_TUNNEL(out)->tun_id = htonl(tun_id);
+ } else {
+ ofputil_put_NXAST_SET_TUNNEL64(out)->tun_id = htonll(tun_id);
+ }
+}
+
+static void
+ofpact_note_to_nxast(const struct ofpact_note *note, struct ofpbuf *out)
+{
+ size_t start_ofs = out->size;
+ struct nx_action_note *nan;
+ unsigned int remainder;
+ unsigned int len;
+
+ nan = ofputil_put_NXAST_NOTE(out);
+ out->size -= sizeof nan->note;
+
+ ofpbuf_put(out, note->data, note->length);
+
+ len = out->size - start_ofs;
+ remainder = len % OFP_ACTION_ALIGN;
+ if (remainder) {
+ ofpbuf_put_zeros(out, OFP_ACTION_ALIGN - remainder);
+ }
+ nan = (struct nx_action_note *)((char *)out->data + start_ofs);
+ nan->len = htons(out->size - start_ofs);
+}
+
+static void
+ofpact_controller_to_nxast(const struct ofpact_controller *oc,
+ struct ofpbuf *out)
+{
+ struct nx_action_controller *nac;
+
+ nac = ofputil_put_NXAST_CONTROLLER(out);
+ nac->max_len = htons(oc->max_len);
+ nac->controller_id = htons(oc->controller_id);
+ nac->reason = oc->reason;
+}
+
+static void
+ofpact_fin_timeout_to_nxast(const struct ofpact_fin_timeout *fin_timeout,
+ struct ofpbuf *out)
+{
+ struct nx_action_fin_timeout *naft = ofputil_put_NXAST_FIN_TIMEOUT(out);
+ naft->fin_idle_timeout = htons(fin_timeout->fin_idle_timeout);
+ naft->fin_hard_timeout = htons(fin_timeout->fin_hard_timeout);
+}
+
+static void
+ofpact_to_nxast(const struct ofpact *a, struct ofpbuf *out)
+{
+ switch (a->type) {
+ case OFPACT_CONTROLLER:
+ ofpact_controller_to_nxast(ofpact_get_CONTROLLER(a), out);
+ break;
+
+ case OFPACT_OUTPUT_REG:
+ ofpact_output_reg_to_nxast(ofpact_get_OUTPUT_REG(a), out);
+ break;
+
+ case OFPACT_BUNDLE:
+ bundle_to_nxast(ofpact_get_BUNDLE(a), out);
+ break;
+
+ case OFPACT_REG_MOVE:
+ nxm_reg_move_to_nxast(ofpact_get_REG_MOVE(a), out);
+ break;
+
+ case OFPACT_REG_LOAD:
+ nxm_reg_load_to_nxast(ofpact_get_REG_LOAD(a), out);
+ break;
+
+ case OFPACT_DEC_TTL:
+ ofputil_put_NXAST_DEC_TTL(out);
+ break;
+
+ case OFPACT_SET_TUNNEL:
+ ofpact_set_tunnel_to_nxast(ofpact_get_SET_TUNNEL(a), out);
+ break;
+
+ case OFPACT_SET_QUEUE:
+ ofputil_put_NXAST_SET_QUEUE(out)->queue_id
+ = htonl(ofpact_get_SET_QUEUE(a)->queue_id);
+ break;
+
+ case OFPACT_POP_QUEUE:
+ ofputil_put_NXAST_POP_QUEUE(out);
+ break;
+
+ case OFPACT_FIN_TIMEOUT:
+ ofpact_fin_timeout_to_nxast(ofpact_get_FIN_TIMEOUT(a), out);
+ break;
+
+ case OFPACT_RESUBMIT:
+ ofpact_resubmit_to_nxast(ofpact_get_RESUBMIT(a), out);
+ break;
+
+ case OFPACT_LEARN:
+ learn_to_nxast(ofpact_get_LEARN(a), out);
+ break;
+
+ case OFPACT_MULTIPATH:
+ multipath_to_nxast(ofpact_get_MULTIPATH(a), out);
+ break;
+
+ case OFPACT_AUTOPATH:
+ autopath_to_nxast(ofpact_get_AUTOPATH(a), out);
+ break;
+
+ case OFPACT_NOTE:
+ ofpact_note_to_nxast(ofpact_get_NOTE(a), out);
+ break;
+
+ case OFPACT_EXIT:
+ ofputil_put_NXAST_EXIT(out);
+ break;
+
+ case OFPACT_OUTPUT:
+ case OFPACT_ENQUEUE:
+ case OFPACT_SET_VLAN_VID:
+ case OFPACT_SET_VLAN_PCP:
+ case OFPACT_STRIP_VLAN:
+ case OFPACT_SET_ETH_SRC:
+ case OFPACT_SET_ETH_DST:
+ case OFPACT_SET_IPV4_SRC:
+ case OFPACT_SET_IPV4_DST:
+ case OFPACT_SET_IPV4_DSCP:
+ case OFPACT_SET_L4_SRC_PORT:
+ case OFPACT_SET_L4_DST_PORT:
+ NOT_REACHED();
+ }
+}
+\f
+/* Converting ofpacts to OpenFlow 1.0. */
+
+static void
+ofpact_output_to_openflow10(const struct ofpact_output *output,
+ struct ofpbuf *out)
+{
+ struct ofp_action_output *oao;
+
+ oao = ofputil_put_OFPAT10_OUTPUT(out);
+ oao->port = htons(output->port);
+ oao->max_len = htons(output->max_len);
+}
+
+static void
+ofpact_enqueue_to_openflow10(const struct ofpact_enqueue *enqueue,
+ struct ofpbuf *out)
+{
+ struct ofp_action_enqueue *oae;
+
+ oae = ofputil_put_OFPAT10_ENQUEUE(out);
+ oae->port = htons(enqueue->port);
+ oae->queue_id = htonl(enqueue->queue);
+}
+
+static void
+ofpact_to_openflow10(const struct ofpact *a, struct ofpbuf *out)
+{
+ switch (a->type) {
+ case OFPACT_OUTPUT:
+ ofpact_output_to_openflow10(ofpact_get_OUTPUT(a), out);
+ break;
+
+ case OFPACT_ENQUEUE:
+ ofpact_enqueue_to_openflow10(ofpact_get_ENQUEUE(a), out);
+ break;
+
+ case OFPACT_SET_VLAN_VID:
+ ofputil_put_OFPAT10_SET_VLAN_VID(out)->vlan_vid
+ = htons(ofpact_get_SET_VLAN_VID(a)->vlan_vid);
+ break;
+
+ case OFPACT_SET_VLAN_PCP:
+ ofputil_put_OFPAT10_SET_VLAN_PCP(out)->vlan_pcp
+ = ofpact_get_SET_VLAN_PCP(a)->vlan_pcp;
+ break;
+
+ case OFPACT_STRIP_VLAN:
+ ofputil_put_OFPAT10_STRIP_VLAN(out);
+ break;
+
+ case OFPACT_SET_ETH_SRC:
+ memcpy(ofputil_put_OFPAT10_SET_DL_SRC(out)->dl_addr,
+ ofpact_get_SET_ETH_SRC(a)->mac, ETH_ADDR_LEN);
+ break;
+
+ case OFPACT_SET_ETH_DST:
+ memcpy(ofputil_put_OFPAT10_SET_DL_DST(out)->dl_addr,
+ ofpact_get_SET_ETH_DST(a)->mac, ETH_ADDR_LEN);
+ break;
+
+ case OFPACT_SET_IPV4_SRC:
+ ofputil_put_OFPAT10_SET_NW_SRC(out)->nw_addr
+ = ofpact_get_SET_IPV4_SRC(a)->ipv4;
+ break;
+
+ case OFPACT_SET_IPV4_DST:
+ ofputil_put_OFPAT10_SET_NW_DST(out)->nw_addr
+ = ofpact_get_SET_IPV4_DST(a)->ipv4;
+ break;
+
+ case OFPACT_SET_IPV4_DSCP:
+ ofputil_put_OFPAT10_SET_NW_TOS(out)->nw_tos
+ = ofpact_get_SET_IPV4_DSCP(a)->dscp;
+ break;
+
+ case OFPACT_SET_L4_SRC_PORT:
+ ofputil_put_OFPAT10_SET_TP_SRC(out)->tp_port
+ = htons(ofpact_get_SET_L4_SRC_PORT(a)->port);
+ break;
+
+ case OFPACT_SET_L4_DST_PORT:
+ ofputil_put_OFPAT10_SET_TP_DST(out)->tp_port
+ = htons(ofpact_get_SET_L4_DST_PORT(a)->port);
+ break;
+
+ case OFPACT_CONTROLLER:
+ case OFPACT_OUTPUT_REG:
+ 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:
+ case OFPACT_LEARN:
+ case OFPACT_MULTIPATH:
+ case OFPACT_AUTOPATH:
+ case OFPACT_NOTE:
+ case OFPACT_EXIT:
+ ofpact_to_nxast(a, out);
+ break;
+ }
+}
+
+/* Converts the 'ofpacts_len' bytes of ofpacts in 'ofpacts' into OpenFlow
+ * actions in 'openflow', appending the actions to any existing data in
+ * 'openflow'. */
+void
+ofpacts_to_openflow(const struct ofpact ofpacts[], size_t ofpacts_len,
+ struct ofpbuf *openflow)
+{
+ const struct ofpact *a;
+
+ OFPACT_FOR_EACH (a, ofpacts, ofpacts_len) {
+ ofpact_to_openflow10(a, openflow);
+ }
+}
+\f
+/* Returns true if 'action' outputs to 'port', false otherwise. */
+static bool
+ofpact_outputs_to_port(const struct ofpact *ofpact, uint16_t port)
+{
+ switch (ofpact->type) {
+ case OFPACT_OUTPUT:
+ return ofpact_get_OUTPUT(ofpact)->port == port;
+ case OFPACT_ENQUEUE:
+ return ofpact_get_ENQUEUE(ofpact)->port == port;
+ case OFPACT_CONTROLLER:
+ return port == OFPP_CONTROLLER;
+
+ case OFPACT_OUTPUT_REG:
+ case OFPACT_BUNDLE:
+ case OFPACT_SET_VLAN_VID:
+ case OFPACT_SET_VLAN_PCP:
+ case OFPACT_STRIP_VLAN:
+ case OFPACT_SET_ETH_SRC:
+ case OFPACT_SET_ETH_DST:
+ case OFPACT_SET_IPV4_SRC:
+ case OFPACT_SET_IPV4_DST:
+ case OFPACT_SET_IPV4_DSCP:
+ case OFPACT_SET_L4_SRC_PORT:
+ case OFPACT_SET_L4_DST_PORT:
+ 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:
+ case OFPACT_LEARN:
+ case OFPACT_MULTIPATH:
+ case OFPACT_AUTOPATH:
+ case OFPACT_NOTE:
+ case OFPACT_EXIT:
+ default:
+ return false;
+ }
+}
+
+/* Returns true if any action in the 'ofpacts_len' bytes of 'ofpacts' outputs
+ * to 'port', false otherwise. */
+bool
+ofpacts_output_to_port(const struct ofpact *ofpacts, size_t ofpacts_len,
+ uint16_t port)
+{
+ const struct ofpact *a;
+
+ OFPACT_FOR_EACH (a, ofpacts, ofpacts_len) {
+ if (ofpact_outputs_to_port(a, port)) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool
+ofpacts_equal(const struct ofpact *a, size_t a_len,
+ const struct ofpact *b, size_t b_len)
+{
+ return a_len == b_len && !memcmp(a, b, a_len);
+}
+\f
+/* Formatting ofpacts. */
+
+static void
+print_note(const struct ofpact_note *note, struct ds *string)
+{
+ size_t i;
+
+ ds_put_cstr(string, "note:");
+ for (i = 0; i < note->length; i++) {
+ if (i) {
+ ds_put_char(string, '.');
+ }
+ ds_put_format(string, "%02"PRIx8, note->data[i]);
+ }
+}
+
+static void
+print_fin_timeout(const struct ofpact_fin_timeout *fin_timeout,
+ struct ds *s)
+{
+ ds_put_cstr(s, "fin_timeout(");
+ if (fin_timeout->fin_idle_timeout) {
+ ds_put_format(s, "idle_timeout=%"PRIu16",",
+ fin_timeout->fin_idle_timeout);
+ }
+ if (fin_timeout->fin_hard_timeout) {
+ ds_put_format(s, "hard_timeout=%"PRIu16",",
+ fin_timeout->fin_hard_timeout);
+ }
+ ds_chomp(s, ',');
+ ds_put_char(s, ')');
+}
+
+static void
+ofpact_format(const struct ofpact *a, struct ds *s)
+{
+ const struct ofpact_enqueue *enqueue;
+ const struct ofpact_resubmit *resubmit;
+ const struct ofpact_autopath *autopath;
+ const struct ofpact_controller *controller;
+ const struct ofpact_tunnel *tunnel;
+ uint16_t port;
+
+ switch (a->type) {
+ case OFPACT_OUTPUT:
+ port = ofpact_get_OUTPUT(a)->port;
+ if (port < OFPP_MAX) {
+ ds_put_format(s, "output:%"PRIu16, port);
+ } else {
+ ofputil_format_port(port, s);
+ if (port == OFPP_CONTROLLER) {
+ ds_put_format(s, ":%"PRIu16, ofpact_get_OUTPUT(a)->max_len);
+ }
+ }
+ break;
+
+ case OFPACT_CONTROLLER:
+ controller = ofpact_get_CONTROLLER(a);
+ if (controller->reason == OFPR_ACTION &&
+ controller->controller_id == 0) {
+ ds_put_format(s, "CONTROLLER:%"PRIu16,
+ ofpact_get_CONTROLLER(a)->max_len);
+ } else {
+ enum ofp_packet_in_reason reason = controller->reason;
+
+ ds_put_cstr(s, "controller(");
+ if (reason != OFPR_ACTION) {
+ ds_put_format(s, "reason=%s,",
+ ofputil_packet_in_reason_to_string(reason));
+ }
+ if (controller->max_len != UINT16_MAX) {
+ ds_put_format(s, "max_len=%"PRIu16",", controller->max_len);
+ }
+ if (controller->controller_id != 0) {
+ ds_put_format(s, "id=%"PRIu16",", controller->controller_id);
+ }
+ ds_chomp(s, ',');
+ ds_put_char(s, ')');
+ }
+ break;
+
+ case OFPACT_ENQUEUE:
+ enqueue = ofpact_get_ENQUEUE(a);
+ ds_put_format(s, "enqueue:");
+ ofputil_format_port(enqueue->port, s);
+ ds_put_format(s, "q%"PRIu32, enqueue->queue);
+ break;
+
+ case OFPACT_OUTPUT_REG:
+ ds_put_cstr(s, "output:");
+ mf_format_subfield(&ofpact_get_OUTPUT_REG(a)->src, s);
+ break;
+
+ case OFPACT_BUNDLE:
+ bundle_format(ofpact_get_BUNDLE(a), s);
+ break;
+
+ case OFPACT_SET_VLAN_VID:
+ ds_put_format(s, "mod_vlan_vid:%"PRIu16,
+ ofpact_get_SET_VLAN_VID(a)->vlan_vid);
+ break;
+
+ case OFPACT_SET_VLAN_PCP:
+ ds_put_format(s, "mod_vlan_pcp:%"PRIu8,
+ ofpact_get_SET_VLAN_PCP(a)->vlan_pcp);
+ break;
+
+ case OFPACT_STRIP_VLAN:
+ ds_put_cstr(s, "strip_vlan");
+ 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));
+ break;
+
+ case OFPACT_SET_ETH_DST:
+ ds_put_format(s, "mod_dl_dst:"ETH_ADDR_FMT,
+ ETH_ADDR_ARGS(ofpact_get_SET_ETH_DST(a)->mac));
+ break;
+
+ case OFPACT_SET_IPV4_SRC:
+ ds_put_format(s, "mod_nw_src:"IP_FMT,
+ 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));
+ break;
+
+ case OFPACT_SET_IPV4_DSCP:
+ ds_put_format(s, "mod_nw_tos:%d", ofpact_get_SET_IPV4_DSCP(a)->dscp);
+ break;
+
+ case OFPACT_SET_L4_SRC_PORT:
+ ds_put_format(s, "mod_tp_src:%d", ofpact_get_SET_L4_SRC_PORT(a)->port);
+ break;
+
+ case OFPACT_SET_L4_DST_PORT:
+ ds_put_format(s, "mod_tp_dst:%d", ofpact_get_SET_L4_DST_PORT(a)->port);
+ break;
+
+ case OFPACT_REG_MOVE:
+ nxm_format_reg_move(ofpact_get_REG_MOVE(a), s);
+ break;
+
+ case OFPACT_REG_LOAD:
+ nxm_format_reg_load(ofpact_get_REG_LOAD(a), s);
+ break;
+
+ case OFPACT_DEC_TTL:
+ ds_put_cstr(s, "dec_ttl");
+ break;
+
+ case OFPACT_SET_TUNNEL:
+ tunnel = ofpact_get_SET_TUNNEL(a);
+ ds_put_format(s, "set_tunnel%s:%#"PRIx64,
+ (tunnel->tun_id > UINT32_MAX
+ || a->compat == OFPUTIL_NXAST_SET_TUNNEL64 ? "64" : ""),
+ tunnel->tun_id);
+ break;
+
+ case OFPACT_SET_QUEUE:
+ ds_put_format(s, "set_queue:%"PRIu32,
+ ofpact_get_SET_QUEUE(a)->queue_id);
+ break;
+
+ case OFPACT_POP_QUEUE:
+ ds_put_cstr(s, "pop_queue");
+ break;
+
+ case OFPACT_FIN_TIMEOUT:
+ print_fin_timeout(ofpact_get_FIN_TIMEOUT(a), s);
+ break;
+
+ 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);
+ } else {
+ ds_put_format(s, "resubmit(");
+ if (resubmit->in_port != OFPP_IN_PORT) {
+ ofputil_format_port(resubmit->in_port, s);
+ }
+ ds_put_char(s, ',');
+ if (resubmit->table_id != 255) {
+ ds_put_format(s, "%"PRIu8, resubmit->table_id);
+ }
+ ds_put_char(s, ')');
+ }
+ break;
+
+ case OFPACT_LEARN:
+ learn_format(ofpact_get_LEARN(a), s);
+ break;
+
+ case OFPACT_MULTIPATH:
+ multipath_format(ofpact_get_MULTIPATH(a), s);
+ break;
+
+ case OFPACT_AUTOPATH:
+ autopath = ofpact_get_AUTOPATH(a);
+ ds_put_format(s, "autopath(%u,", autopath->port);
+ mf_format_subfield(&autopath->dst, s);
+ ds_put_char(s, ')');
+ break;
+
+ case OFPACT_NOTE:
+ print_note(ofpact_get_NOTE(a), s);
+ break;
+
+ case OFPACT_EXIT:
+ ds_put_cstr(s, "exit");
+ break;
+ }
+}
+
+/* Appends a string representing the 'ofpacts_len' bytes of ofpacts in
+ * 'ofpacts' to 'string'. */
+void
+ofpacts_format(const struct ofpact *ofpacts, size_t ofpacts_len,
+ struct ds *string)
+{
+ ds_put_cstr(string, "actions=");
+ if (!ofpacts_len) {
+ ds_put_cstr(string, "drop");
+ } else {
+ const struct ofpact *a;
+
+ OFPACT_FOR_EACH (a, ofpacts, ofpacts_len) {
+ if (a != ofpacts) {
+ ds_put_cstr(string, ",");
+ }
+ ofpact_format(a, string);
+ }
+ }
+}
+\f
+/* Internal use by helpers. */
+
+void *
+ofpact_put(struct ofpbuf *ofpacts, enum ofpact_type type, size_t len)
+{
+ struct ofpact *ofpact;
+
+ ofpact_pad(ofpacts);
+ ofpact = ofpacts->l2 = ofpbuf_put_uninit(ofpacts, len);
+ ofpact_init(ofpact, type, len);
+ return ofpact;
+}
+
+void
+ofpact_init(struct ofpact *ofpact, enum ofpact_type type, size_t len)
+{
+ memset(ofpact, 0, len);
+ ofpact->type = type;
+ ofpact->compat = OFPUTIL_ACTION_INVALID;
+ ofpact->len = len;
+}
+\f
+/* Updates 'ofpact->len' to the number of bytes in the tail of 'ofpacts'
+ * starting at 'ofpact'.
+ *
+ * This is the correct way to update a variable-length ofpact's length after
+ * adding the variable-length part of the payload. (See the large comment
+ * near the end of ofp-actions.h for more information.) */
+void
+ofpact_update_len(struct ofpbuf *ofpacts, struct ofpact *ofpact)
+{
+ assert(ofpact == ofpacts->l2);
+ ofpact->len = (char *) ofpbuf_tail(ofpacts) - (char *) ofpact;
+}
+
+/* Pads out 'ofpacts' to a multiple of OFPACT_ALIGNTO bytes in length. Each
+ * ofpact_put_<ENUM>() calls this function automatically beforehand, but the
+ * client must call this itself after adding the final ofpact to an array of
+ * them.
+ *
+ * (The consequences of failing to call this function are probably not dire.
+ * OFPACT_FOR_EACH will calculate a pointer beyond the end of the ofpacts, but
+ * not dereference it. That's undefined behavior, technically, but it will not
+ * cause a real problem on common systems. Still, it seems better to call
+ * it.) */
+void
+ofpact_pad(struct ofpbuf *ofpacts)
+{
+ unsigned int rem = ofpacts->size % OFPACT_ALIGNTO;
+ if (rem) {
+ ofpbuf_put_zeros(ofpacts, OFPACT_ALIGNTO - rem);
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2012 Nicira Networks.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef OFP_ACTIONS_H
+#define OFP_ACTIONS_H 1
+
+#include <stdint.h>
+#include "meta-flow.h"
+#include "ofp-errors.h"
+#include "ofp-util.h"
+#include "openflow/openflow.h"
+#include "openflow/nicira-ext.h"
+#include "openvswitch/types.h"
+
+/* List of OVS abstracted actions.
+ *
+ * This macro is used directly only internally by this header, but the list is
+ * still of interest to developers.
+ *
+ * Each DEFINE_OFPACT invocation has the following parameters:
+ *
+ * 1. <ENUM>, used below in the enum definition of OFPACT_<ENUM>, and
+ * elsewhere.
+ *
+ * 2. <STRUCT> corresponding to a structure "struct <STRUCT>", that must be
+ * defined below. This structure must be an abstract definition of the
+ * action. Its first member must have type "struct ofpact" and name
+ * "ofpact". It may be fixed length or end with a flexible array member
+ * (e.g. "int member[];").
+ *
+ * 3. <MEMBER>, which has one of two possible values:
+ *
+ * - If "struct <STRUCT>" is fixed-length, it must be "ofpact".
+ *
+ * - If "struct <STRUCT>" is variable-length, it must be the name of the
+ * flexible array member.
+ */
+#define OFPACTS \
+ /* Output. */ \
+ DEFINE_OFPACT(OUTPUT, ofpact_output, ofpact) \
+ DEFINE_OFPACT(CONTROLLER, ofpact_controller, ofpact) \
+ DEFINE_OFPACT(ENQUEUE, ofpact_enqueue, ofpact) \
+ DEFINE_OFPACT(OUTPUT_REG, ofpact_output_reg, ofpact) \
+ DEFINE_OFPACT(BUNDLE, ofpact_bundle, slaves) \
+ \
+ /* Header changes. */ \
+ DEFINE_OFPACT(SET_VLAN_VID, ofpact_vlan_vid, ofpact) \
+ DEFINE_OFPACT(SET_VLAN_PCP, ofpact_vlan_pcp, ofpact) \
+ DEFINE_OFPACT(STRIP_VLAN, ofpact_null, ofpact) \
+ DEFINE_OFPACT(SET_ETH_SRC, ofpact_mac, ofpact) \
+ DEFINE_OFPACT(SET_ETH_DST, ofpact_mac, ofpact) \
+ DEFINE_OFPACT(SET_IPV4_SRC, ofpact_ipv4, ofpact) \
+ DEFINE_OFPACT(SET_IPV4_DST, ofpact_ipv4, ofpact) \
+ DEFINE_OFPACT(SET_IPV4_DSCP, ofpact_dscp, ofpact) \
+ DEFINE_OFPACT(SET_L4_SRC_PORT, ofpact_l4_port, ofpact) \
+ DEFINE_OFPACT(SET_L4_DST_PORT, ofpact_l4_port, ofpact) \
+ DEFINE_OFPACT(REG_MOVE, ofpact_reg_move, ofpact) \
+ DEFINE_OFPACT(REG_LOAD, ofpact_reg_load, ofpact) \
+ DEFINE_OFPACT(DEC_TTL, ofpact_null, ofpact) \
+ \
+ /* Metadata. */ \
+ DEFINE_OFPACT(SET_TUNNEL, ofpact_tunnel, ofpact) \
+ DEFINE_OFPACT(SET_QUEUE, ofpact_queue, ofpact) \
+ DEFINE_OFPACT(POP_QUEUE, ofpact_null, ofpact) \
+ DEFINE_OFPACT(FIN_TIMEOUT, ofpact_fin_timeout, ofpact) \
+ \
+ /* Flow table interaction. */ \
+ DEFINE_OFPACT(RESUBMIT, ofpact_resubmit, ofpact) \
+ DEFINE_OFPACT(LEARN, ofpact_learn, specs) \
+ \
+ /* Arithmetic. */ \
+ DEFINE_OFPACT(MULTIPATH, ofpact_multipath, ofpact) \
+ DEFINE_OFPACT(AUTOPATH, ofpact_autopath, ofpact) \
+ \
+ /* Other. */ \
+ DEFINE_OFPACT(NOTE, ofpact_note, data) \
+ DEFINE_OFPACT(EXIT, ofpact_null, ofpact)
+
+/* enum ofpact_type, with a member OFPACT_<ENUM> for each action. */
+enum OVS_PACKED_ENUM ofpact_type {
+#define DEFINE_OFPACT(ENUM, STRUCT, MEMBER) OFPACT_##ENUM,
+ OFPACTS
+#undef DEFINE_OFPACT
+};
+
+/* N_OFPACTS, the number of values of "enum ofpact_type". */
+enum {
+ N_OFPACTS =
+#define DEFINE_OFPACT(ENUM, STRUCT, MEMBER) + 1
+ OFPACTS
+#undef DEFINE_OFPACT
+};
+
+/* Header for an action.
+ *
+ * Each action is a structure "struct ofpact_*" that begins with "struct
+ * ofpact", usually followed by other data that describes the action. Actions
+ * are padded out to a multiple of OFPACT_ALIGNTO bytes in length. */
+struct ofpact {
+ enum ofpact_type type; /* OFPACT_*. */
+ enum ofputil_action_code compat; /* Original type when added, if any. */
+ uint16_t len; /* Length of the action, in bytes, including
+ * struct ofpact, excluding padding. */
+};
+
+#ifdef __GNUC__
+/* Make sure that OVS_PACKED_ENUM really worked. */
+BUILD_ASSERT_DECL(sizeof(struct ofpact) == 4);
+#endif
+
+/* Alignment. */
+#define OFPACT_ALIGNTO 8
+#define OFPACT_ALIGN(SIZE) ROUND_UP(SIZE, OFPACT_ALIGNTO)
+
+static inline struct ofpact *
+ofpact_next(const struct ofpact *ofpact)
+{
+ return (void *) ((uint8_t *) ofpact + OFPACT_ALIGN(ofpact->len));
+}
+
+static inline struct ofpact *
+ofpact_end(const struct ofpact *ofpacts, size_t ofpacts_len)
+{
+ return (void *) ((uint8_t *) ofpacts + ofpacts_len);
+}
+
+/* Assigns POS to each ofpact, in turn, in the OFPACTS_LEN bytes of ofpacts
+ * starting at OFPACTS. */
+#define OFPACT_FOR_EACH(POS, OFPACTS, OFPACTS_LEN) \
+ for ((POS) = (OFPACTS); (POS) < ofpact_end(OFPACTS, OFPACTS_LEN); \
+ (POS) = ofpact_next(POS))
+\f
+/* Action structure for each OFPACT_*. */
+
+/* OFPACT_STRIP_VLAN, OFPACT_DEC_TTL, OFPACT_POP_QUEUE, OFPACT_EXIT.
+ *
+ * Used for OFPAT10_STRIP_VLAN, NXAST_DEC_TTL, NXAST_POP_QUEUE, NXAST_EXIT.
+ *
+ * Action structure for actions that do not have any extra data beyond the
+ * action type. */
+struct ofpact_null {
+ struct ofpact ofpact;
+};
+
+/* OFPACT_OUTPUT.
+ *
+ * Used for OFPAT10_OUTPUT. */
+struct ofpact_output {
+ struct ofpact ofpact;
+ uint16_t port; /* Output port. */
+ uint16_t max_len; /* Max send len, for port OFPP_CONTROLLER. */
+};
+
+/* OFPACT_CONTROLLER.
+ *
+ * Used for NXAST_CONTROLLER. */
+struct ofpact_controller {
+ struct ofpact ofpact;
+ uint16_t max_len; /* Maximum length to send to controller. */
+ uint16_t controller_id; /* Controller ID to send packet-in. */
+ enum ofp_packet_in_reason reason; /* Reason to put in packet-in. */
+};
+
+/* OFPACT_ENQUEUE.
+ *
+ * Used for OFPAT10_ENQUEUE. */
+struct ofpact_enqueue {
+ struct ofpact ofpact;
+ uint16_t port;
+ uint32_t queue;
+};
+
+/* OFPACT_OUTPUT_REG.
+ *
+ * Used for NXAST_OUTPUT_REG. */
+struct ofpact_output_reg {
+ struct ofpact ofpact;
+ struct mf_subfield src;
+ uint16_t max_len;
+};
+
+/* OFPACT_BUNDLE.
+ *
+ * Used for NXAST_BUNDLE. */
+struct ofpact_bundle {
+ struct ofpact ofpact;
+
+ /* Slave choice algorithm to apply to hash value. */
+ enum nx_bd_algorithm algorithm;
+
+ /* What fields to hash and how. */
+ enum nx_hash_fields fields;
+ uint16_t basis; /* Universal hash parameter. */
+
+ struct mf_subfield dst;
+
+ /* Slaves for output. */
+ unsigned int n_slaves;
+ uint16_t slaves[];
+};
+
+/* OFPACT_SET_VLAN_VID.
+ *
+ * Used for OFPAT10_SET_VLAN_VID. */
+struct ofpact_vlan_vid {
+ struct ofpact ofpact;
+ uint16_t vlan_vid; /* VLAN VID in low 12 bits, 0 in other bits. */
+};
+
+/* OFPACT_SET_VLAN_PCP.
+ *
+ * Used for OFPAT10_SET_VLAN_PCP. */
+struct ofpact_vlan_pcp {
+ struct ofpact ofpact;
+ uint8_t vlan_pcp; /* VLAN PCP in low 3 bits, 0 in other bits. */
+};
+
+/* OFPACT_SET_ETH_SRC, OFPACT_SET_ETH_DST.
+ *
+ * Used for OFPAT10_SET_DL_SRC, OFPAT10_SET_DL_DST. */
+struct ofpact_mac {
+ struct ofpact ofpact;
+ uint8_t mac[ETH_ADDR_LEN];
+};
+
+/* OFPACT_SET_IPV4_SRC, OFPACT_SET_IPV4_DST.
+ *
+ * Used for OFPAT10_SET_NW_SRC, OFPAT10_SET_NW_DST. */
+struct ofpact_ipv4 {
+ struct ofpact ofpact;
+ ovs_be32 ipv4;
+};
+
+/* OFPACT_SET_IPV4_DSCP.
+ *
+ * Used for OFPAT10_SET_NW_TOS. */
+struct ofpact_dscp {
+ struct ofpact ofpact;
+ uint8_t dscp; /* DSCP in high 6 bits, rest ignored. */
+};
+
+/* OFPACT_SET_L4_SRC_PORT, OFPACT_SET_L4_DST_PORT.
+ *
+ * Used for OFPAT10_SET_TP_SRC, OFPAT10_SET_TP_DST. */
+struct ofpact_l4_port {
+ struct ofpact ofpact;
+ uint16_t port; /* TCP or UDP port number. */
+};
+
+/* OFPACT_REG_MOVE.
+ *
+ * Used for NXAST_REG_MOVE. */
+struct ofpact_reg_move {
+ struct ofpact ofpact;
+ struct mf_subfield src;
+ struct mf_subfield dst;
+};
+
+/* OFPACT_REG_LOAD.
+ *
+ * Used for NXAST_REG_LOAD. */
+struct ofpact_reg_load {
+ struct ofpact ofpact;
+ struct mf_subfield dst;
+ uint64_t value;
+};
+
+/* OFPACT_SET_TUNNEL.
+ *
+ * Used for NXAST_SET_TUNNEL, NXAST_SET_TUNNEL64. */
+struct ofpact_tunnel {
+ struct ofpact ofpact;
+ uint64_t tun_id;
+};
+
+/* OFPACT_SET_QUEUE.
+ *
+ * Used for NXAST_SET_QUEUE. */
+struct ofpact_queue {
+ struct ofpact ofpact;
+ uint32_t queue_id;
+};
+
+/* OFPACT_FIN_TIMEOUT.
+ *
+ * Used for NXAST_FIN_TIMEOUT. */
+struct ofpact_fin_timeout {
+ struct ofpact ofpact;
+ uint16_t fin_idle_timeout;
+ uint16_t fin_hard_timeout;
+};
+
+/* OFPACT_RESUBMIT.
+ *
+ * Used for NXAST_RESUBMIT, NXAST_RESUBMIT_TABLE. */
+struct ofpact_resubmit {
+ struct ofpact ofpact;
+ uint16_t in_port;
+ uint8_t table_id;
+};
+
+/* Part of struct ofpact_learn, below. */
+struct ofpact_learn_spec {
+ int n_bits;
+
+ int src_type;
+ struct mf_subfield src;
+ union mf_subvalue src_imm;
+
+ int dst_type;
+ struct mf_subfield dst;
+};
+
+/* OFPACT_LEARN.
+ *
+ * Used for NXAST_LEARN. */
+struct ofpact_learn {
+ struct ofpact ofpact;
+
+ uint16_t idle_timeout; /* Idle time before discarding (seconds). */
+ uint16_t hard_timeout; /* Max time before discarding (seconds). */
+ uint16_t priority; /* Priority level of flow entry. */
+ uint64_t cookie; /* Cookie for new flow. */
+ uint16_t flags; /* Either 0 or OFPFF_SEND_FLOW_REM. */
+ uint8_t table_id; /* Table to insert flow entry. */
+ uint16_t fin_idle_timeout; /* Idle timeout after FIN, if nonzero. */
+ uint16_t fin_hard_timeout; /* Hard timeout after FIN, if nonzero. */
+
+ unsigned int n_specs;
+ struct ofpact_learn_spec specs[];
+};
+
+/* OFPACT_MULTIPATH.
+ *
+ * Used for NXAST_MULTIPATH. */
+struct ofpact_multipath {
+ struct ofpact ofpact;
+
+ /* What fields to hash and how. */
+ enum nx_hash_fields fields;
+ uint16_t basis; /* Universal hash parameter. */
+
+ /* Multipath link choice algorithm to apply to hash value. */
+ enum nx_mp_algorithm algorithm;
+ uint16_t max_link; /* Number of output links, minus 1. */
+ uint32_t arg; /* Algorithm-specific argument. */
+
+ /* Where to store the result. */
+ struct mf_subfield dst;
+};
+
+/* OFPACT_AUTOPATH.
+ *
+ * Used for NXAST_AUTOPATH. */
+struct ofpact_autopath {
+ struct ofpact ofpact;
+ struct mf_subfield dst;
+ uint32_t port;
+};
+
+/* OFPACT_NOTE.
+ *
+ * Used for NXAST_NOTE. */
+struct ofpact_note {
+ struct ofpact ofpact;
+ size_t length;
+ uint8_t data[];
+};
+
+/* Converting OpenFlow to ofpacts. */
+enum ofperr ofpacts_pull_openflow(struct ofpbuf *openflow,
+ unsigned int actions_len,
+ struct ofpbuf *ofpacts);
+enum ofperr ofpacts_check(const struct ofpact[], size_t ofpacts_len,
+ const struct flow *, int max_ports);
+
+/* Converting ofpacts to OpenFlow. */
+void ofpacts_to_openflow(const struct ofpact[], size_t ofpacts_len,
+ struct ofpbuf *openflow);
+
+/* Working with ofpacts. */
+bool ofpacts_output_to_port(const struct ofpact[], size_t ofpacts_len,
+ uint16_t port);
+bool ofpacts_equal(const struct ofpact a[], size_t a_len,
+ const struct ofpact b[], size_t b_len);
+
+/* Formatting ofpacts.
+ *
+ * (For parsing ofpacts, see ofp-parse.h.) */
+void ofpacts_format(const struct ofpact[], size_t ofpacts_len, struct ds *);
+
+/* Internal use by the helpers below. */
+void ofpact_init(struct ofpact *, enum ofpact_type, size_t len);
+void *ofpact_put(struct ofpbuf *, enum ofpact_type, size_t len);
+
+/* For each OFPACT_<ENUM> with a corresponding struct <STRUCT>, this defines
+ * the following commonly useful functions:
+ *
+ * struct <STRUCT> *ofpact_put_<ENUM>(struct ofpbuf *ofpacts);
+ *
+ * Appends a new 'ofpact', of length OFPACT_<ENUM>_RAW_SIZE, to 'ofpacts',
+ * initializes it with ofpact_init_<ENUM>(), and returns it. Also sets
+ * 'ofpacts->l2' to the returned action.
+ *
+ * After using this function to add a variable-length action, add the
+ * elements of the flexible array (e.g. with ofpbuf_put()), then use
+ * ofpact_update_len() to update the length embedded into the action.
+ * (Keep in mind the need to refresh the structure from 'ofpacts->l2' after
+ * adding data to 'ofpacts'.)
+ *
+ * struct <STRUCT> *ofpact_get_<ENUM>(const struct ofpact *ofpact);
+ *
+ * Returns 'ofpact' cast to "struct <STRUCT> *". 'ofpact->type' must be
+ * OFPACT_<ENUM>.
+ *
+ * as well as the following more rarely useful definitions:
+ *
+ * void ofpact_init_<ENUM>(struct <STRUCT> *ofpact);
+ *
+ * Initializes the parts of 'ofpact' that identify it as having type
+ * OFPACT_<ENUM> and length OFPACT_<ENUM>_RAW_SIZE and zeros the rest.
+ *
+ * <ENUM>_RAW_SIZE
+ *
+ * The size of the action structure. For a fixed-length action, this is
+ * sizeof(struct <STRUCT>). For a variable-length action, this is the
+ * offset to the variable-length part.
+ *
+ * <ENUM>_SIZE
+ *
+ * An integer constant, the value of OFPACT_<ENUM>_RAW_SIZE rounded up to a
+ * multiple of OFPACT_ALIGNTO.
+ */
+#define DEFINE_OFPACT(ENUM, STRUCT, MEMBER) \
+ BUILD_ASSERT_DECL(offsetof(struct STRUCT, ofpact) == 0); \
+ \
+ enum { OFPACT_##ENUM##_RAW_SIZE \
+ = (offsetof(struct STRUCT, MEMBER) \
+ ? offsetof(struct STRUCT, MEMBER) \
+ : sizeof(struct STRUCT)) }; \
+ \
+ enum { OFPACT_##ENUM##_SIZE \
+ = ROUND_UP(OFPACT_##ENUM##_RAW_SIZE, OFPACT_ALIGNTO) }; \
+ \
+ static inline struct STRUCT * \
+ ofpact_get_##ENUM(const struct ofpact *ofpact) \
+ { \
+ assert(ofpact->type == OFPACT_##ENUM); \
+ return (struct STRUCT *) ofpact; \
+ } \
+ \
+ static inline struct STRUCT * \
+ ofpact_put_##ENUM(struct ofpbuf *ofpacts) \
+ { \
+ return ofpact_put(ofpacts, OFPACT_##ENUM, \
+ OFPACT_##ENUM##_RAW_SIZE); \
+ } \
+ \
+ static inline void \
+ ofpact_init_##ENUM(struct STRUCT *ofpact) \
+ { \
+ ofpact_init(&ofpact->ofpact, OFPACT_##ENUM, \
+ OFPACT_##ENUM##_RAW_SIZE); \
+ }
+OFPACTS
+#undef DEFINE_OFPACT
+
+/* Functions to use after adding ofpacts to a buffer. */
+void ofpact_update_len(struct ofpbuf *, struct ofpact *);
+void ofpact_pad(struct ofpbuf *);
+
+#endif /* ofp-actions.h */
#include "dynamic-string.h"
#include "learn.h"
#include "meta-flow.h"
-#include "netdev.h"
#include "multipath.h"
+#include "netdev.h"
#include "nx-match.h"
+#include "ofp-actions.h"
#include "ofp-util.h"
#include "ofpbuf.h"
#include "openflow/openflow.h"
*ip = in_addr.s_addr;
}
-static struct ofp_action_output *
-put_output_action(struct ofpbuf *b, uint16_t port)
-{
- struct ofp_action_output *oao;
-
- oao = ofputil_put_OFPAT10_OUTPUT(b);
- oao->port = htons(port);
- return oao;
-}
-
static void
-parse_enqueue(struct ofpbuf *b, char *arg)
+parse_enqueue(char *arg, struct ofpbuf *ofpacts)
{
char *sp = NULL;
char *port = strtok_r(arg, ":q", &sp);
char *queue = strtok_r(NULL, "", &sp);
- struct ofp_action_enqueue *oae;
+ struct ofpact_enqueue *enqueue;
if (port == NULL || queue == NULL) {
ovs_fatal(0, "\"enqueue\" syntax is \"enqueue:PORT:QUEUE\"");
}
- oae = ofputil_put_OFPAT10_ENQUEUE(b);
- oae->port = htons(str_to_u32(port));
- oae->queue_id = htonl(str_to_u32(queue));
+ enqueue = ofpact_put_ENQUEUE(ofpacts);
+ enqueue->port = str_to_u32(port);
+ enqueue->queue = str_to_u32(queue);
}
static void
-parse_output(struct ofpbuf *b, char *arg)
+parse_output(char *arg, struct ofpbuf *ofpacts)
{
if (strchr(arg, '[')) {
- struct nx_action_output_reg *naor;
- struct mf_subfield src;
-
- mf_parse_subfield(&src, arg);
+ struct ofpact_output_reg *output_reg;
- naor = ofputil_put_NXAST_OUTPUT_REG(b);
- naor->ofs_nbits = nxm_encode_ofs_nbits(src.ofs, src.n_bits);
- naor->src = htonl(src.field->nxm_header);
- naor->max_len = htons(UINT16_MAX);
+ output_reg = ofpact_put_OUTPUT_REG(ofpacts);
+ mf_parse_subfield(&output_reg->src, arg);
+ output_reg->max_len = UINT16_MAX;
} else {
- put_output_action(b, str_to_u32(arg));
+ struct ofpact_output *output;
+
+ output = ofpact_put_OUTPUT(ofpacts);
+ output->port = str_to_u32(arg);
+ output->max_len = output->port == OFPP_CONTROLLER ? UINT16_MAX : 0;
}
}
static void
-parse_resubmit(struct ofpbuf *b, char *arg)
+parse_resubmit(char *arg, struct ofpbuf *ofpacts)
{
- struct nx_action_resubmit *nar;
+ struct ofpact_resubmit *resubmit;
char *in_port_s, *table_s;
- uint16_t in_port;
- uint8_t table;
+
+ resubmit = ofpact_put_RESUBMIT(ofpacts);
in_port_s = strsep(&arg, ",");
if (in_port_s && in_port_s[0]) {
- if (!ofputil_port_from_string(in_port_s, &in_port)) {
- in_port = str_to_u32(in_port_s);
+ if (!ofputil_port_from_string(in_port_s, &resubmit->in_port)) {
+ resubmit->in_port = str_to_u32(in_port_s);
}
} else {
- in_port = OFPP_IN_PORT;
+ resubmit->in_port = OFPP_IN_PORT;
}
table_s = strsep(&arg, ",");
- table = table_s && table_s[0] ? str_to_u32(table_s) : 255;
+ resubmit->table_id = table_s && table_s[0] ? str_to_u32(table_s) : 255;
- if (in_port == OFPP_IN_PORT && table == 255) {
+ if (resubmit->in_port == OFPP_IN_PORT && resubmit->table_id == 255) {
ovs_fatal(0, "at least one \"in_port\" or \"table\" must be specified "
" on resubmit");
}
-
- if (in_port != OFPP_IN_PORT && table == 255) {
- nar = ofputil_put_NXAST_RESUBMIT(b);
- } else {
- nar = ofputil_put_NXAST_RESUBMIT_TABLE(b);
- nar->table = table;
- }
- nar->in_port = htons(in_port);
-}
-
-static void
-parse_set_tunnel(struct ofpbuf *b, const char *arg)
-{
- uint64_t tun_id = str_to_u64(arg);
- if (tun_id > UINT32_MAX) {
- ofputil_put_NXAST_SET_TUNNEL64(b)->tun_id = htonll(tun_id);
- } else {
- ofputil_put_NXAST_SET_TUNNEL(b)->tun_id = htonl(tun_id);
- }
}
static void
-parse_note(struct ofpbuf *b, const char *arg)
+parse_note(const char *arg, struct ofpbuf *ofpacts)
{
- size_t start_ofs = b->size;
- struct nx_action_note *nan;
- int remainder;
- size_t len;
+ struct ofpact_note *note;
- nan = ofputil_put_NXAST_NOTE(b);
-
- b->size -= sizeof nan->note;
+ note = ofpact_put_NOTE(ofpacts);
while (*arg != '\0') {
uint8_t byte;
bool ok;
if (!ok) {
ovs_fatal(0, "bad hex digit in `note' argument");
}
- ofpbuf_put(b, &byte, 1);
+ ofpbuf_put(ofpacts, &byte, 1);
- arg += 2;
- }
+ note = ofpacts->l2;
+ note->length++;
- len = b->size - start_ofs;
- remainder = len % OFP_ACTION_ALIGN;
- if (remainder) {
- ofpbuf_put_zeros(b, OFP_ACTION_ALIGN - remainder);
+ arg += 2;
}
- nan = (struct nx_action_note *)((char *)b->data + start_ofs);
- nan->len = htons(b->size - start_ofs);
+ ofpact_update_len(ofpacts, ¬e->ofpact);
}
static void
parse_fin_timeout(struct ofpbuf *b, char *arg)
{
- struct nx_action_fin_timeout *naft;
+ struct ofpact_fin_timeout *oft = ofpact_put_FIN_TIMEOUT(b);
char *key, *value;
- naft = ofputil_put_NXAST_FIN_TIMEOUT(b);
while (ofputil_parse_key_value(&arg, &key, &value)) {
if (!strcmp(key, "idle_timeout")) {
- naft->fin_idle_timeout = htons(str_to_u16(value, key));
+ oft->fin_idle_timeout = str_to_u16(value, key);
} else if (!strcmp(key, "hard_timeout")) {
- naft->fin_hard_timeout = htons(str_to_u16(value, key));
+ oft->fin_hard_timeout = str_to_u16(value, key);
} else {
ovs_fatal(0, "invalid key '%s' in 'fin_timeout' argument", key);
}
}
if (reason == OFPR_ACTION && controller_id == 0) {
- put_output_action(b, OFPP_CONTROLLER)->max_len = htons(max_len);
+ struct ofpact_output *output;
+
+ output = ofpact_put_OUTPUT(b);
+ output->port = OFPP_CONTROLLER;
+ output->max_len = max_len;
} else {
- struct nx_action_controller *nac;
+ struct ofpact_controller *controller;
- nac = ofputil_put_NXAST_CONTROLLER(b);
- nac->max_len = htons(max_len);
- nac->reason = reason;
- nac->controller_id = htons(controller_id);
+ controller = ofpact_put_CONTROLLER(b);
+ controller->max_len = max_len;
+ controller->reason = reason;
+ controller->controller_id = controller_id;
}
}
static void
parse_named_action(enum ofputil_action_code code, const struct flow *flow,
- struct ofpbuf *b, char *arg)
+ char *arg, struct ofpbuf *ofpacts)
{
- struct ofp_action_dl_addr *oada;
- struct ofp_action_vlan_pcp *oavp;
- struct ofp_action_vlan_vid *oavv;
- struct ofp_action_nw_addr *oana;
- struct ofp_action_tp_port *oata;
+ struct ofpact_tunnel *tunnel;
+ uint16_t vid;
+ ovs_be32 ip;
+ uint8_t pcp;
+ uint8_t tos;
switch (code) {
case OFPUTIL_ACTION_INVALID:
NOT_REACHED();
case OFPUTIL_OFPAT10_OUTPUT:
- parse_output(b, arg);
+ parse_output(arg, ofpacts);
break;
case OFPUTIL_OFPAT10_SET_VLAN_VID:
- oavv = ofputil_put_OFPAT10_SET_VLAN_VID(b);
- oavv->vlan_vid = htons(str_to_u32(arg));
+ vid = str_to_u32(arg);
+ if (vid & ~VLAN_VID_MASK) {
+ ovs_fatal(0, "%s: not a valid VLAN VID", arg);
+ }
+ ofpact_put_SET_VLAN_VID(ofpacts)->vlan_vid = vid;
break;
case OFPUTIL_OFPAT10_SET_VLAN_PCP:
- oavp = ofputil_put_OFPAT10_SET_VLAN_PCP(b);
- oavp->vlan_pcp = str_to_u32(arg);
+ pcp = str_to_u32(arg);
+ if (pcp & ~7) {
+ ovs_fatal(0, "%s: not a valid VLAN PCP", arg);
+ }
+ ofpact_put_SET_VLAN_PCP(ofpacts)->vlan_pcp = pcp;
break;
case OFPUTIL_OFPAT10_STRIP_VLAN:
- ofputil_put_OFPAT10_STRIP_VLAN(b);
+ ofpact_put_STRIP_VLAN(ofpacts);
break;
case OFPUTIL_OFPAT10_SET_DL_SRC:
+ str_to_mac(arg, ofpact_put_SET_ETH_SRC(ofpacts)->mac);
+ break;
+
case OFPUTIL_OFPAT10_SET_DL_DST:
- oada = ofputil_put_action(code, b);
- str_to_mac(arg, oada->dl_addr);
+ str_to_mac(arg, ofpact_put_SET_ETH_DST(ofpacts)->mac);
break;
case OFPUTIL_OFPAT10_SET_NW_SRC:
+ str_to_ip(arg, &ip);
+ ofpact_put_SET_IPV4_SRC(ofpacts)->ipv4 = ip;
+ break;
+
case OFPUTIL_OFPAT10_SET_NW_DST:
- oana = ofputil_put_action(code, b);
- str_to_ip(arg, &oana->nw_addr);
+ str_to_ip(arg, &ip);
+ ofpact_put_SET_IPV4_DST(ofpacts)->ipv4 = ip;
break;
case OFPUTIL_OFPAT10_SET_NW_TOS:
- ofputil_put_OFPAT10_SET_NW_TOS(b)->nw_tos = str_to_u32(arg);
+ tos = str_to_u32(arg);
+ if (tos & ~IP_DSCP_MASK) {
+ ovs_fatal(0, "%s: not a valid TOS", arg);
+ }
+ ofpact_put_SET_IPV4_DSCP(ofpacts)->dscp = tos;
break;
case OFPUTIL_OFPAT10_SET_TP_SRC:
+ ofpact_put_SET_L4_SRC_PORT(ofpacts)->port = str_to_u32(arg);
+ break;
+
case OFPUTIL_OFPAT10_SET_TP_DST:
- oata = ofputil_put_action(code, b);
- oata->tp_port = htons(str_to_u32(arg));
+ ofpact_put_SET_L4_DST_PORT(ofpacts)->port = str_to_u32(arg);
break;
case OFPUTIL_OFPAT10_ENQUEUE:
- parse_enqueue(b, arg);
+ parse_enqueue(arg, ofpacts);
break;
case OFPUTIL_NXAST_RESUBMIT:
- parse_resubmit(b, arg);
+ parse_resubmit(arg, ofpacts);
break;
case OFPUTIL_NXAST_SET_TUNNEL:
- parse_set_tunnel(b, arg);
+ case OFPUTIL_NXAST_SET_TUNNEL64:
+ tunnel = ofpact_put_SET_TUNNEL(ofpacts);
+ tunnel->ofpact.compat = code;
+ tunnel->tun_id = str_to_u64(arg);
break;
case OFPUTIL_NXAST_SET_QUEUE:
- ofputil_put_NXAST_SET_QUEUE(b)->queue_id = htonl(str_to_u32(arg));
+ ofpact_put_SET_QUEUE(ofpacts)->queue_id = str_to_u32(arg);
break;
case OFPUTIL_NXAST_POP_QUEUE:
- ofputil_put_NXAST_POP_QUEUE(b);
+ ofpact_put_POP_QUEUE(ofpacts);
break;
case OFPUTIL_NXAST_REG_MOVE:
- nxm_parse_reg_move(ofputil_put_NXAST_REG_MOVE(b), arg);
+ nxm_parse_reg_move(ofpact_put_REG_MOVE(ofpacts), arg);
break;
case OFPUTIL_NXAST_REG_LOAD:
- nxm_parse_reg_load(ofputil_put_NXAST_REG_LOAD(b), arg);
+ nxm_parse_reg_load(ofpact_put_REG_LOAD(ofpacts), arg);
break;
case OFPUTIL_NXAST_NOTE:
- parse_note(b, arg);
- break;
-
- case OFPUTIL_NXAST_SET_TUNNEL64:
- ofputil_put_NXAST_SET_TUNNEL64(b)->tun_id = htonll(str_to_u64(arg));
+ parse_note(arg, ofpacts);
break;
case OFPUTIL_NXAST_MULTIPATH:
- multipath_parse(ofputil_put_NXAST_MULTIPATH(b), arg);
+ multipath_parse(ofpact_put_MULTIPATH(ofpacts), arg);
break;
case OFPUTIL_NXAST_AUTOPATH:
- autopath_parse(ofputil_put_NXAST_AUTOPATH(b), arg);
+ autopath_parse(ofpact_put_AUTOPATH(ofpacts), arg);
break;
case OFPUTIL_NXAST_BUNDLE:
- bundle_parse(b, arg);
+ bundle_parse(arg, ofpacts);
break;
case OFPUTIL_NXAST_BUNDLE_LOAD:
- bundle_parse_load(b, arg);
+ bundle_parse_load(arg, ofpacts);
break;
case OFPUTIL_NXAST_RESUBMIT_TABLE:
NOT_REACHED();
case OFPUTIL_NXAST_LEARN:
- learn_parse(b, arg, flow);
+ learn_parse(arg, flow, ofpacts);
break;
case OFPUTIL_NXAST_EXIT:
- ofputil_put_NXAST_EXIT(b);
+ ofpact_put_EXIT(ofpacts);
break;
case OFPUTIL_NXAST_DEC_TTL:
- ofputil_put_NXAST_DEC_TTL(b);
+ ofpact_put_DEC_TTL(ofpacts);
break;
case OFPUTIL_NXAST_FIN_TIMEOUT:
- parse_fin_timeout(b, arg);
+ parse_fin_timeout(ofpacts, arg);
break;
case OFPUTIL_NXAST_CONTROLLER:
- parse_controller(b, arg);
+ parse_controller(ofpacts, arg);
break;
}
}
static void
-str_to_action(const struct flow *flow, char *str, struct ofpbuf *b)
+str_to_ofpacts(const struct flow *flow, char *str, struct ofpbuf *ofpacts)
{
char *pos, *act, *arg;
int n_actions;
code = ofputil_action_code_from_name(act);
if (code >= 0) {
- parse_named_action(code, flow, b, arg);
+ parse_named_action(code, flow, arg, ofpacts);
} else if (!strcasecmp(act, "drop")) {
- /* A drop action in OpenFlow occurs by just not setting
- * an action. */
if (n_actions) {
ovs_fatal(0, "Drop actions must not be preceded by other "
"actions");
}
break;
} else if (ofputil_port_from_string(act, &port)) {
- put_output_action(b, port);
+ ofpact_put_OUTPUT(ofpacts)->port = port;
} else {
ovs_fatal(0, "Unknown action: %s", act);
}
n_actions++;
}
+ ofpact_pad(ofpacts);
}
struct protocol {
fm->new_cookie = htonll(0);
}
if (fields & F_ACTIONS) {
- struct ofpbuf actions;
+ struct ofpbuf ofpacts;
- ofpbuf_init(&actions, sizeof(union ofp_action));
- str_to_action(&fm->cr.flow, act_str, &actions);
- fm->actions = ofpbuf_steal_data(&actions);
- fm->n_actions = actions.size / sizeof(union ofp_action);
+ ofpbuf_init(&ofpacts, 32);
+ str_to_ofpacts(&fm->cr.flow, act_str, &ofpacts);
+ fm->ofpacts_len = ofpacts.size;
+ fm->ofpacts = ofpbuf_steal_data(&ofpacts);
} else {
- fm->actions = NULL;
- fm->n_actions = 0;
+ fm->ofpacts_len = 0;
+ fm->ofpacts = NULL;
}
free(string);
* Prints an error on stderr and aborts the program if 's' syntax is
* invalid. */
void
-parse_ofp_actions(const char *s_, struct ofpbuf *actions)
+parse_ofpacts(const char *s_, struct ofpbuf *ofpacts)
{
char *s = xstrdup(s_);
- str_to_action(NULL, s, actions);
+ str_to_ofpacts(NULL, s, ofpacts);
free(s);
}
bool aggregate, const char *string);
-void parse_ofp_actions(const char *, struct ofpbuf *actions);
+void parse_ofpacts(const char *, struct ofpbuf *ofpacts);
char *parse_ofp_exact_flow(struct flow *, const char *);
#include "meta-flow.h"
#include "netdev.h"
#include "nx-match.h"
+#include "ofp-actions.h"
#include "ofp-errors.h"
#include "ofp-util.h"
#include "ofpbuf.h"
}
}
-static void
-print_note(struct ds *string, const struct nx_action_note *nan)
-{
- size_t len;
- size_t i;
-
- ds_put_cstr(string, "note:");
- len = ntohs(nan->len) - offsetof(struct nx_action_note, note);
- for (i = 0; i < len; i++) {
- if (i) {
- ds_put_char(string, '.');
- }
- ds_put_format(string, "%02"PRIx8, nan->note[i]);
- }
-}
-
-static void
-ofp_print_action(struct ds *s, const union ofp_action *a,
- enum ofputil_action_code code)
-{
- const struct ofp_action_enqueue *oae;
- const struct ofp_action_dl_addr *oada;
- const struct nx_action_set_tunnel64 *nast64;
- const struct nx_action_set_tunnel *nast;
- const struct nx_action_set_queue *nasq;
- const struct nx_action_resubmit *nar;
- const struct nx_action_reg_move *move;
- const struct nx_action_reg_load *load;
- const struct nx_action_multipath *nam;
- const struct nx_action_autopath *naa;
- const struct nx_action_output_reg *naor;
- const struct nx_action_fin_timeout *naft;
- const struct nx_action_controller *nac;
- struct mf_subfield subfield;
- uint16_t port;
-
- switch (code) {
- case OFPUTIL_ACTION_INVALID:
- NOT_REACHED();
-
- case OFPUTIL_OFPAT10_OUTPUT:
- port = ntohs(a->output.port);
- if (port < OFPP_MAX) {
- ds_put_format(s, "output:%"PRIu16, port);
- } else {
- ofputil_format_port(port, s);
- if (port == OFPP_CONTROLLER) {
- if (a->output.max_len != htons(0)) {
- ds_put_format(s, ":%"PRIu16, ntohs(a->output.max_len));
- } else {
- ds_put_cstr(s, ":all");
- }
- }
- }
- break;
-
- case OFPUTIL_OFPAT10_ENQUEUE:
- oae = (const struct ofp_action_enqueue *) a;
- ds_put_format(s, "enqueue:");
- ofputil_format_port(ntohs(oae->port), s);
- ds_put_format(s, "q%"PRIu32, ntohl(oae->queue_id));
- break;
-
- case OFPUTIL_OFPAT10_SET_VLAN_VID:
- ds_put_format(s, "mod_vlan_vid:%"PRIu16,
- ntohs(a->vlan_vid.vlan_vid));
- break;
-
- case OFPUTIL_OFPAT10_SET_VLAN_PCP:
- ds_put_format(s, "mod_vlan_pcp:%"PRIu8, a->vlan_pcp.vlan_pcp);
- break;
-
- case OFPUTIL_OFPAT10_STRIP_VLAN:
- ds_put_cstr(s, "strip_vlan");
- break;
-
- case OFPUTIL_OFPAT10_SET_DL_SRC:
- oada = (const struct ofp_action_dl_addr *) a;
- ds_put_format(s, "mod_dl_src:"ETH_ADDR_FMT,
- ETH_ADDR_ARGS(oada->dl_addr));
- break;
-
- case OFPUTIL_OFPAT10_SET_DL_DST:
- oada = (const struct ofp_action_dl_addr *) a;
- ds_put_format(s, "mod_dl_dst:"ETH_ADDR_FMT,
- ETH_ADDR_ARGS(oada->dl_addr));
- break;
-
- case OFPUTIL_OFPAT10_SET_NW_SRC:
- ds_put_format(s, "mod_nw_src:"IP_FMT, IP_ARGS(&a->nw_addr.nw_addr));
- break;
-
- case OFPUTIL_OFPAT10_SET_NW_DST:
- ds_put_format(s, "mod_nw_dst:"IP_FMT, IP_ARGS(&a->nw_addr.nw_addr));
- break;
-
- case OFPUTIL_OFPAT10_SET_NW_TOS:
- ds_put_format(s, "mod_nw_tos:%d", a->nw_tos.nw_tos);
- break;
-
- case OFPUTIL_OFPAT10_SET_TP_SRC:
- ds_put_format(s, "mod_tp_src:%d", ntohs(a->tp_port.tp_port));
- break;
-
- case OFPUTIL_OFPAT10_SET_TP_DST:
- ds_put_format(s, "mod_tp_dst:%d", ntohs(a->tp_port.tp_port));
- break;
-
- case OFPUTIL_NXAST_RESUBMIT:
- nar = (struct nx_action_resubmit *)a;
- ds_put_format(s, "resubmit:");
- ofputil_format_port(ntohs(nar->in_port), s);
- break;
-
- case OFPUTIL_NXAST_RESUBMIT_TABLE:
- nar = (struct nx_action_resubmit *)a;
- ds_put_format(s, "resubmit(");
- if (nar->in_port != htons(OFPP_IN_PORT)) {
- ofputil_format_port(ntohs(nar->in_port), s);
- }
- ds_put_char(s, ',');
- if (nar->table != 255) {
- ds_put_format(s, "%"PRIu8, nar->table);
- }
- ds_put_char(s, ')');
- break;
-
- case OFPUTIL_NXAST_SET_TUNNEL:
- nast = (struct nx_action_set_tunnel *)a;
- ds_put_format(s, "set_tunnel:%#"PRIx32, ntohl(nast->tun_id));
- break;
-
- case OFPUTIL_NXAST_SET_QUEUE:
- nasq = (struct nx_action_set_queue *)a;
- ds_put_format(s, "set_queue:%u", ntohl(nasq->queue_id));
- break;
-
- case OFPUTIL_NXAST_POP_QUEUE:
- ds_put_cstr(s, "pop_queue");
- break;
-
- case OFPUTIL_NXAST_NOTE:
- print_note(s, (const struct nx_action_note *) a);
- break;
-
- case OFPUTIL_NXAST_REG_MOVE:
- move = (const struct nx_action_reg_move *) a;
- nxm_format_reg_move(move, s);
- break;
-
- case OFPUTIL_NXAST_REG_LOAD:
- load = (const struct nx_action_reg_load *) a;
- nxm_format_reg_load(load, s);
- break;
-
- case OFPUTIL_NXAST_SET_TUNNEL64:
- nast64 = (const struct nx_action_set_tunnel64 *) a;
- ds_put_format(s, "set_tunnel64:%#"PRIx64,
- ntohll(nast64->tun_id));
- break;
-
- case OFPUTIL_NXAST_MULTIPATH:
- nam = (const struct nx_action_multipath *) a;
- multipath_format(nam, s);
- break;
-
- case OFPUTIL_NXAST_AUTOPATH:
- naa = (const struct nx_action_autopath *)a;
- ds_put_format(s, "autopath(%u,", ntohl(naa->id));
- nxm_decode(&subfield, naa->dst, naa->ofs_nbits);
- mf_format_subfield(&subfield, s);
- ds_put_char(s, ')');
- break;
-
- case OFPUTIL_NXAST_BUNDLE:
- case OFPUTIL_NXAST_BUNDLE_LOAD:
- bundle_format((const struct nx_action_bundle *) a, s);
- break;
-
- case OFPUTIL_NXAST_OUTPUT_REG:
- naor = (const struct nx_action_output_reg *) a;
- ds_put_cstr(s, "output:");
- nxm_decode(&subfield, naor->src, naor->ofs_nbits);
- mf_format_subfield(&subfield, s);
- break;
-
- case OFPUTIL_NXAST_LEARN:
- learn_format((const struct nx_action_learn *) a, s);
- break;
-
- case OFPUTIL_NXAST_DEC_TTL:
- ds_put_cstr(s, "dec_ttl");
- break;
-
- case OFPUTIL_NXAST_EXIT:
- ds_put_cstr(s, "exit");
- break;
-
- case OFPUTIL_NXAST_FIN_TIMEOUT:
- naft = (const struct nx_action_fin_timeout *) a;
- ds_put_cstr(s, "fin_timeout(");
- if (naft->fin_idle_timeout) {
- ds_put_format(s, "idle_timeout=%"PRIu16",",
- ntohs(naft->fin_idle_timeout));
- }
- if (naft->fin_hard_timeout) {
- ds_put_format(s, "hard_timeout=%"PRIu16",",
- ntohs(naft->fin_hard_timeout));
- }
- ds_chomp(s, ',');
- ds_put_char(s, ')');
- break;
-
- case OFPUTIL_NXAST_CONTROLLER:
- nac = (const struct nx_action_controller *) a;
- ds_put_cstr(s, "controller(");
- if (nac->reason != OFPR_ACTION) {
- ds_put_format(s, "reason=%s,",
- ofputil_packet_in_reason_to_string(nac->reason));
- }
- if (nac->max_len != htons(UINT16_MAX)) {
- ds_put_format(s, "max_len=%"PRIu16",", ntohs(nac->max_len));
- }
- if (nac->controller_id != htons(0)) {
- ds_put_format(s, "id=%"PRIu16",", ntohs(nac->controller_id));
- }
- ds_chomp(s, ',');
- ds_put_char(s, ')');
- break;
-
- default:
- break;
- }
-}
-
-void
-ofp_print_actions(struct ds *string, const union ofp_action *actions,
- size_t n_actions)
-{
- const union ofp_action *a;
- size_t left;
-
- ds_put_cstr(string, "actions=");
- if (!n_actions) {
- ds_put_cstr(string, "drop");
- }
-
- OFPUTIL_ACTION_FOR_EACH (a, left, actions, n_actions) {
- int code = ofputil_decode_action(a);
- if (code >= 0) {
- if (a != actions) {
- ds_put_cstr(string, ",");
- }
- ofp_print_action(string, a, code);
- } else {
- ofp_print_error(string, -code);
- }
- }
- if (left > 0) {
- ds_put_format(string, " ***%zu leftover bytes following actions",
- left * sizeof *a);
- }
-}
-
static void
ofp_print_packet_out(struct ds *string, const struct ofp_packet_out *opo,
int verbosity)
{
struct ofputil_packet_out po;
+ struct ofpbuf ofpacts;
enum ofperr error;
- error = ofputil_decode_packet_out(&po, opo);
+ ofpbuf_init(&ofpacts, 64);
+ error = ofputil_decode_packet_out(&po, opo, &ofpacts);
if (error) {
+ ofpbuf_uninit(&ofpacts);
ofp_print_error(string, error);
return;
}
ofputil_format_port(po.in_port, string);
ds_put_char(string, ' ');
- ofp_print_actions(string, po.actions, po.n_actions);
+ ofpacts_format(po.ofpacts, po.ofpacts_len, string);
if (po.buffer_id == UINT32_MAX) {
ds_put_format(string, " data_len=%zu", po.packet_len);
ds_put_format(string, " buffer=0x%08"PRIx32, po.buffer_id);
}
ds_put_char(string, '\n');
+
+ ofpbuf_uninit(&ofpacts);
}
/* qsort comparison function. */
enum ofputil_msg_code code, int verbosity)
{
struct ofputil_flow_mod fm;
+ struct ofpbuf ofpacts;
bool need_priority;
enum ofperr error;
- error = ofputil_decode_flow_mod(&fm, oh, OFPUTIL_P_OF10_TID);
+ ofpbuf_init(&ofpacts, 64);
+ error = ofputil_decode_flow_mod(&fm, oh, OFPUTIL_P_OF10_TID, &ofpacts);
if (error) {
+ ofpbuf_uninit(&ofpacts);
ofp_print_error(s, error);
return;
}
}
}
- ofp_print_actions(s, fm.actions, fm.n_actions);
+ ofpacts_format(fm.ofpacts, fm.ofpacts_len, s);
+ ofpbuf_uninit(&ofpacts);
}
static void
static void
ofp_print_flow_stats_reply(struct ds *string, const struct ofp_header *oh)
{
+ struct ofpbuf ofpacts;
struct ofpbuf b;
ofpbuf_use_const(&b, oh, ntohs(oh->length));
+ ofpbuf_init(&ofpacts, 64);
for (;;) {
struct ofputil_flow_stats fs;
int retval;
- retval = ofputil_decode_flow_stats_reply(&fs, &b, true);
+ retval = ofputil_decode_flow_stats_reply(&fs, &b, true, &ofpacts);
if (retval) {
if (retval != EOF) {
ds_put_cstr(string, " ***parse error***");
if (string->string[string->length - 1] != ' ') {
ds_put_char(string, ' ');
}
- ofp_print_actions(string, fs.actions, fs.n_actions);
- }
+ ofpacts_format(fs.ofpacts, fs.ofpacts_len, string);
+ }
+ ofpbuf_uninit(&ofpacts);
}
static void
struct ofp_flow_mod;
struct ofp10_match;
struct ds;
-union ofp_action;
#ifdef __cplusplus
extern "C" {
void ofp_print(FILE *, const void *, size_t, int verbosity);
void ofp_print_packet(FILE *stream, const void *data, size_t len);
-void ofp_print_actions(struct ds *, const union ofp_action *, size_t);
void ofp10_match_print(struct ds *, const struct ofp10_match *, int verbosity);
char *ofp_to_string(const void *, size_t, int verbosity);
#include "multipath.h"
#include "netdev.h"
#include "nx-match.h"
+#include "ofp-actions.h"
#include "ofp-errors.h"
#include "ofp-util.h"
#include "ofpbuf.h"
* flow_mod in 'fm'. Returns 0 if successful, otherwise an OpenFlow error
* code.
*
- * Does not validate the flow_mod actions. */
+ * Uses 'ofpacts' to store the abstract OFPACT_* version of 'oh''s actions.
+ * The caller must initialize 'ofpacts' and retains ownership of it.
+ * 'fm->ofpacts' will point into the 'ofpacts' buffer.
+ *
+ * Does not validate the flow_mod actions. The caller should do that, with
+ * ofpacts_check(). */
enum ofperr
ofputil_decode_flow_mod(struct ofputil_flow_mod *fm,
const struct ofp_header *oh,
- enum ofputil_protocol protocol)
+ enum ofputil_protocol protocol,
+ struct ofpbuf *ofpacts)
{
const struct ofputil_msg_type *type;
uint16_t command;
uint16_t priority;
enum ofperr error;
- /* Dissect the message. */
+ /* Get the ofp_flow_mod. */
ofm = ofpbuf_pull(&b, sizeof *ofm);
- error = ofputil_pull_actions(&b, b.size, &fm->actions, &fm->n_actions);
- if (error) {
- return error;
- }
/* Set priority based on original wildcards. Normally we'd allow
* ofputil_cls_rule_from_match() to do this for us, but
ofputil_cls_rule_from_ofp10_match(&ofm->match, priority, &fm->cr);
ofputil_normalize_rule(&fm->cr);
+ /* Now get the actions. */
+ error = ofpacts_pull_openflow(&b, b.size, ofpacts);
+ if (error) {
+ return error;
+ }
+
/* Translate the message. */
command = ntohs(ofm->command);
fm->cookie = htonll(0);
if (error) {
return error;
}
- error = ofputil_pull_actions(&b, b.size, &fm->actions, &fm->n_actions);
+ error = ofpacts_pull_openflow(&b, b.size, ofpacts);
if (error) {
return error;
}
NOT_REACHED();
}
+ fm->ofpacts = ofpacts->data;
+ fm->ofpacts_len = ofpacts->size;
if (protocol & OFPUTIL_P_TID) {
fm->command = command & 0xff;
fm->table_id = command >> 8;
ofputil_encode_flow_mod(const struct ofputil_flow_mod *fm,
enum ofputil_protocol protocol)
{
- size_t actions_len = fm->n_actions * sizeof *fm->actions;
struct ofp_flow_mod *ofm;
struct nx_flow_mod *nfm;
struct ofpbuf *msg;
switch (protocol) {
case OFPUTIL_P_OF10:
case OFPUTIL_P_OF10_TID:
- msg = ofpbuf_new(sizeof *ofm + actions_len);
+ msg = ofpbuf_new(sizeof *ofm + fm->ofpacts_len);
ofm = put_openflow(sizeof *ofm, OFPT_FLOW_MOD, msg);
ofputil_cls_rule_to_ofp10_match(&fm->cr, &ofm->match);
ofm->cookie = fm->new_cookie;
case OFPUTIL_P_NXM:
case OFPUTIL_P_NXM_TID:
- msg = ofpbuf_new(sizeof *nfm + NXM_TYPICAL_LEN + actions_len);
+ msg = ofpbuf_new(sizeof *nfm + NXM_TYPICAL_LEN + fm->ofpacts_len);
put_nxmsg(sizeof *nfm, NXT_FLOW_MOD, msg);
nfm = msg->data;
nfm->command = htons(command);
NOT_REACHED();
}
- ofpbuf_put(msg, fm->actions, actions_len);
+ if (fm->ofpacts) {
+ ofpacts_to_openflow(fm->ofpacts, fm->ofpacts_len, msg);
+ }
update_openflow_length(msg);
return msg;
}
* 'flow_age_extension' as true so that the contents of 'msg' determine the
* 'idle_age' and 'hard_age' members in 'fs'.
*
+ * Uses 'ofpacts' to store the abstract OFPACT_* version of the flow stats
+ * reply's actions. The caller must initialize 'ofpacts' and retains ownership
+ * of it. 'fs->ofpacts' will point into the 'ofpacts' buffer.
+ *
* Returns 0 if successful, EOF if no replies were left in this 'msg',
* otherwise a positive errno value. */
int
ofputil_decode_flow_stats_reply(struct ofputil_flow_stats *fs,
struct ofpbuf *msg,
- bool flow_age_extension)
+ bool flow_age_extension,
+ struct ofpbuf *ofpacts)
{
const struct ofputil_msg_type *type;
int code;
return EINVAL;
}
- if (ofputil_pull_actions(msg, length - sizeof *ofs,
- &fs->actions, &fs->n_actions)) {
+ if (ofpacts_pull_openflow(msg, length - sizeof *ofs, ofpacts)) {
return EINVAL;
}
fs->byte_count = ntohll(get_32aligned_be64(&ofs->byte_count));
} else if (code == OFPUTIL_NXST_FLOW_REPLY) {
const struct nx_flow_stats *nfs;
- size_t match_len, length;
+ size_t match_len, actions_len, length;
nfs = ofpbuf_try_pull(msg, sizeof *nfs);
if (!nfs) {
return EINVAL;
}
- if (ofputil_pull_actions(msg,
- length - sizeof *nfs - ROUND_UP(match_len, 8),
- &fs->actions, &fs->n_actions)) {
+ actions_len = length - sizeof *nfs - ROUND_UP(match_len, 8);
+ if (ofpacts_pull_openflow(msg, actions_len, ofpacts)) {
return EINVAL;
}
NOT_REACHED();
}
+ fs->ofpacts = ofpacts->data;
+ fs->ofpacts_len = ofpacts->size;
+
return 0;
}
ofputil_append_flow_stats_reply(const struct ofputil_flow_stats *fs,
struct list *replies)
{
- size_t act_len = fs->n_actions * sizeof *fs->actions;
- const struct ofp_stats_msg *osm;
+ struct ofpbuf *reply = ofpbuf_from_list(list_back(replies));
+ const struct ofp_stats_msg *osm = reply->data;
+ size_t start_ofs = reply->size;
- osm = ofpbuf_from_list(list_back(replies))->data;
if (osm->type == htons(OFPST_FLOW)) {
- size_t len = offsetof(struct ofp_flow_stats, actions) + act_len;
struct ofp_flow_stats *ofs;
- ofs = ofputil_append_stats_reply(len, replies);
- ofs->length = htons(len);
+ ofs = ofpbuf_put_uninit(reply, sizeof *ofs);
ofs->table_id = fs->table_id;
ofs->pad = 0;
ofputil_cls_rule_to_ofp10_match(&fs->rule, &ofs->match);
htonll(unknown_to_zero(fs->packet_count)));
put_32aligned_be64(&ofs->byte_count,
htonll(unknown_to_zero(fs->byte_count)));
- memcpy(ofs->actions, fs->actions, act_len);
+ ofpacts_to_openflow(fs->ofpacts, fs->ofpacts_len, reply);
+
+ ofs = ofpbuf_at_assert(reply, start_ofs, sizeof *ofs);
+ ofs->length = htons(reply->size - start_ofs);
} else if (osm->type == htons(OFPST_VENDOR)) {
struct nx_flow_stats *nfs;
- struct ofpbuf *msg;
- size_t start_len;
-
- msg = ofputil_reserve_stats_reply(
- sizeof *nfs + NXM_MAX_LEN + act_len, replies);
- start_len = msg->size;
- nfs = ofpbuf_put_uninit(msg, sizeof *nfs);
+ nfs = ofpbuf_put_uninit(reply, sizeof *nfs);
nfs->table_id = fs->table_id;
nfs->pad = 0;
nfs->duration_sec = htonl(fs->duration_sec);
nfs->hard_age = htons(fs->hard_age < 0 ? 0
: fs->hard_age < UINT16_MAX ? fs->hard_age + 1
: UINT16_MAX);
- nfs->match_len = htons(nx_put_match(msg, false, &fs->rule, 0, 0));
+ nfs->match_len = htons(nx_put_match(reply, false, &fs->rule, 0, 0));
nfs->cookie = fs->cookie;
nfs->packet_count = htonll(fs->packet_count);
nfs->byte_count = htonll(fs->byte_count);
- ofpbuf_put(msg, fs->actions, act_len);
- nfs->length = htons(msg->size - start_len);
+ ofpacts_to_openflow(fs->ofpacts, fs->ofpacts_len, reply);
+
+ nfs = ofpbuf_at_assert(reply, start_ofs, sizeof *nfs);
+ nfs->length = htons(reply->size - start_ofs);
} else {
NOT_REACHED();
}
+
+ ofputil_postappend_stats_reply(start_ofs, replies);
}
/* Converts abstract ofputil_aggregate_stats 'stats' into an OFPST_AGGREGATE or
return false;
}
+/* Converts an OFPT_PACKET_OUT in 'opo' into an abstract ofputil_packet_out in
+ * 'po'.
+ *
+ * Uses 'ofpacts' to store the abstract OFPACT_* version of the packet out
+ * message's actions. The caller must initialize 'ofpacts' and retains
+ * ownership of it. 'po->ofpacts' will point into the 'ofpacts' buffer.
+ *
+ * Returns 0 if successful, otherwise an OFPERR_* value. */
enum ofperr
ofputil_decode_packet_out(struct ofputil_packet_out *po,
- const struct ofp_packet_out *opo)
+ const struct ofp_packet_out *opo,
+ struct ofpbuf *ofpacts)
{
enum ofperr error;
struct ofpbuf b;
ofpbuf_use_const(&b, opo, ntohs(opo->header.length));
ofpbuf_pull(&b, sizeof *opo);
- error = ofputil_pull_actions(&b, ntohs(opo->actions_len),
- &po->actions, &po->n_actions);
+ error = ofpacts_pull_openflow(&b, ntohs(opo->actions_len), ofpacts);
if (error) {
return error;
}
+ po->ofpacts = ofpacts->data;
+ po->ofpacts_len = ofpacts->size;
if (po->buffer_id == UINT32_MAX) {
po->packet = b.data;
ofputil_encode_packet_out(const struct ofputil_packet_out *po)
{
struct ofp_packet_out *opo;
- size_t actions_len;
struct ofpbuf *msg;
size_t size;
- actions_len = po->n_actions * sizeof *po->actions;
- size = sizeof *opo + actions_len;
+ size = sizeof *opo + po->ofpacts_len;
if (po->buffer_id == UINT32_MAX) {
size += po->packet_len;
}
msg = ofpbuf_new(size);
- opo = put_openflow(sizeof *opo, OFPT_PACKET_OUT, msg);
+ put_openflow(sizeof *opo, OFPT_PACKET_OUT, msg);
+ ofpacts_to_openflow(po->ofpacts, po->ofpacts_len, msg);
+
+ opo = msg->data;
opo->buffer_id = htonl(po->buffer_id);
opo->in_port = htons(po->in_port);
- opo->actions_len = htons(actions_len);
- ofpbuf_put(msg, po->actions, actions_len);
+ opo->actions_len = htons(msg->size - sizeof *opo);
+
if (po->buffer_id == UINT32_MAX) {
ofpbuf_put(msg, po->packet, po->packet_len);
}
+
update_openflow_length(msg);
return msg;
return ofpbuf_put_uninit(ofputil_reserve_stats_reply(len, replies), len);
}
+void
+ofputil_postappend_stats_reply(size_t start_ofs, struct list *replies)
+{
+ struct ofpbuf *msg = ofpbuf_from_list(list_back(replies));
+
+ assert(start_ofs <= UINT16_MAX);
+ if (msg->size > UINT16_MAX) {
+ size_t len = msg->size - start_ofs;
+ memcpy(ofputil_append_stats_reply(len, replies),
+ (const uint8_t *) msg->data + start_ofs, len);
+ msg->size = start_ofs;
+ }
+}
+
/* Returns the first byte past the ofp_stats_msg header in 'oh'. */
const void *
ofputil_stats_body(const struct ofp_header *oh)
return ntohs(oh->length) - sizeof(struct nicira_stats_msg);
}
-struct ofpbuf *
-make_flow_mod(uint16_t command, const struct cls_rule *rule,
- size_t actions_len)
-{
- struct ofp_flow_mod *ofm;
- size_t size = sizeof *ofm + actions_len;
- struct ofpbuf *out = ofpbuf_new(size);
- ofm = ofpbuf_put_zeros(out, sizeof *ofm);
- ofm->header.version = OFP10_VERSION;
- ofm->header.type = OFPT_FLOW_MOD;
- ofm->header.length = htons(size);
- ofm->cookie = 0;
- ofm->priority = htons(MIN(rule->priority, UINT16_MAX));
- ofputil_cls_rule_to_ofp10_match(rule, &ofm->match);
- ofm->command = htons(command);
- return out;
-}
-
-struct ofpbuf *
-make_add_flow(const struct cls_rule *rule, uint32_t buffer_id,
- uint16_t idle_timeout, size_t actions_len)
-{
- struct ofpbuf *out = make_flow_mod(OFPFC_ADD, rule, actions_len);
- struct ofp_flow_mod *ofm = out->data;
- ofm->idle_timeout = htons(idle_timeout);
- ofm->hard_timeout = htons(OFP_FLOW_PERMANENT);
- ofm->buffer_id = htonl(buffer_id);
- return out;
-}
-
-struct ofpbuf *
-make_packet_in(uint32_t buffer_id, uint16_t in_port, uint8_t reason,
- const struct ofpbuf *payload, int max_send_len)
-{
- struct ofp_packet_in *opi;
- struct ofpbuf *buf;
- int send_len;
-
- send_len = MIN(max_send_len, payload->size);
- buf = ofpbuf_new(sizeof *opi + send_len);
- opi = put_openflow_xid(offsetof(struct ofp_packet_in, data),
- OFPT_PACKET_IN, 0, buf);
- opi->buffer_id = htonl(buffer_id);
- opi->total_len = htons(payload->size);
- opi->in_port = htons(in_port);
- opi->reason = reason;
- ofpbuf_put(buf, payload->data, send_len);
- update_openflow_length(buf);
-
- return buf;
-}
-
/* Creates and returns an OFPT_ECHO_REQUEST message with an empty payload. */
struct ofpbuf *
make_echo_request(void)
return b->size / ofputil_get_phy_port_size(ofp_version);
}
-static enum ofperr
-check_resubmit_table(const struct nx_action_resubmit *nar)
-{
- if (nar->pad[0] || nar->pad[1] || nar->pad[2]) {
- return OFPERR_OFPBAC_BAD_ARGUMENT;
- }
- return 0;
-}
-
-static enum ofperr
-check_output_reg(const struct nx_action_output_reg *naor,
- const struct flow *flow)
-{
- struct mf_subfield src;
- size_t i;
-
- for (i = 0; i < sizeof naor->zero; i++) {
- if (naor->zero[i]) {
- return OFPERR_OFPBAC_BAD_ARGUMENT;
- }
- }
-
- nxm_decode(&src, naor->src, naor->ofs_nbits);
- return mf_check_src(&src, flow);
-}
-
-enum ofperr
-validate_actions(const union ofp_action *actions, size_t n_actions,
- const struct flow *flow, int max_ports)
-{
- const union ofp_action *a;
- size_t left;
-
- OFPUTIL_ACTION_FOR_EACH (a, left, actions, n_actions) {
- enum ofperr error;
- uint16_t port;
- int code;
-
- code = ofputil_decode_action(a);
- if (code < 0) {
- error = -code;
- VLOG_WARN_RL(&bad_ofmsg_rl,
- "action decoding error at offset %td (%s)",
- (a - actions) * sizeof *a, ofperr_get_name(error));
-
- return error;
- }
-
- error = 0;
- switch ((enum ofputil_action_code) code) {
- case OFPUTIL_ACTION_INVALID:
- NOT_REACHED();
-
- case OFPUTIL_OFPAT10_OUTPUT:
- error = ofputil_check_output_port(ntohs(a->output.port),
- max_ports);
- break;
-
- case OFPUTIL_OFPAT10_SET_VLAN_VID:
- if (a->vlan_vid.vlan_vid & ~htons(0xfff)) {
- error = OFPERR_OFPBAC_BAD_ARGUMENT;
- }
- break;
-
- case OFPUTIL_OFPAT10_SET_VLAN_PCP:
- if (a->vlan_pcp.vlan_pcp & ~7) {
- error = OFPERR_OFPBAC_BAD_ARGUMENT;
- }
- break;
-
- case OFPUTIL_OFPAT10_ENQUEUE:
- port = ntohs(((const struct ofp_action_enqueue *) a)->port);
- if (port >= max_ports && port != OFPP_IN_PORT
- && port != OFPP_LOCAL) {
- error = OFPERR_OFPBAC_BAD_OUT_PORT;
- }
- break;
-
- case OFPUTIL_NXAST_REG_MOVE:
- error = nxm_check_reg_move((const struct nx_action_reg_move *) a,
- flow);
- break;
-
- case OFPUTIL_NXAST_REG_LOAD:
- error = nxm_check_reg_load((const struct nx_action_reg_load *) a,
- flow);
- break;
-
- case OFPUTIL_NXAST_MULTIPATH:
- error = multipath_check((const struct nx_action_multipath *) a,
- flow);
- break;
-
- case OFPUTIL_NXAST_AUTOPATH:
- error = autopath_check((const struct nx_action_autopath *) a,
- flow);
- break;
-
- case OFPUTIL_NXAST_BUNDLE:
- case OFPUTIL_NXAST_BUNDLE_LOAD:
- error = bundle_check((const struct nx_action_bundle *) a,
- max_ports, flow);
- break;
-
- case OFPUTIL_NXAST_OUTPUT_REG:
- error = check_output_reg((const struct nx_action_output_reg *) a,
- flow);
- break;
-
- case OFPUTIL_NXAST_RESUBMIT_TABLE:
- error = check_resubmit_table(
- (const struct nx_action_resubmit *) a);
- break;
-
- case OFPUTIL_NXAST_LEARN:
- error = learn_check((const struct nx_action_learn *) a, flow);
- break;
-
- case OFPUTIL_NXAST_CONTROLLER:
- if (((const struct nx_action_controller *) a)->zero) {
- error = OFPERR_NXBAC_MUST_BE_ZERO;
- }
- break;
-
- case OFPUTIL_OFPAT10_STRIP_VLAN:
- case OFPUTIL_OFPAT10_SET_NW_SRC:
- case OFPUTIL_OFPAT10_SET_NW_DST:
- case OFPUTIL_OFPAT10_SET_NW_TOS:
- case OFPUTIL_OFPAT10_SET_TP_SRC:
- case OFPUTIL_OFPAT10_SET_TP_DST:
- case OFPUTIL_OFPAT10_SET_DL_SRC:
- case OFPUTIL_OFPAT10_SET_DL_DST:
- case OFPUTIL_NXAST_RESUBMIT:
- case OFPUTIL_NXAST_SET_TUNNEL:
- case OFPUTIL_NXAST_SET_QUEUE:
- case OFPUTIL_NXAST_POP_QUEUE:
- case OFPUTIL_NXAST_NOTE:
- case OFPUTIL_NXAST_SET_TUNNEL64:
- case OFPUTIL_NXAST_EXIT:
- case OFPUTIL_NXAST_DEC_TTL:
- case OFPUTIL_NXAST_FIN_TIMEOUT:
- break;
- }
-
- if (error) {
- VLOG_WARN_RL(&bad_ofmsg_rl, "bad action at offset %td (%s)",
- (a - actions) * sizeof *a, ofperr_get_name(error));
- return error;
- }
- }
- if (left) {
- VLOG_WARN_RL(&bad_ofmsg_rl, "bad action format at offset %zu",
- (n_actions - left) * sizeof *a);
- return OFPERR_OFPBAC_BAD_LEN;
- }
- return 0;
-}
-
-struct ofputil_action {
- int code;
- unsigned int min_len;
- unsigned int max_len;
-};
-
-static const struct ofputil_action action_bad_type
- = { -OFPERR_OFPBAC_BAD_TYPE, 0, UINT_MAX };
-static const struct ofputil_action action_bad_len
- = { -OFPERR_OFPBAC_BAD_LEN, 0, UINT_MAX };
-static const struct ofputil_action action_bad_vendor
- = { -OFPERR_OFPBAC_BAD_VENDOR, 0, UINT_MAX };
-
-static const struct ofputil_action *
-ofputil_decode_ofpat_action(const union ofp_action *a)
-{
- enum ofp10_action_type type = ntohs(a->type);
-
- switch (type) {
-#define OFPAT10_ACTION(ENUM, STRUCT, NAME) \
- case ENUM: { \
- static const struct ofputil_action action = { \
- OFPUTIL_##ENUM, \
- sizeof(struct STRUCT), \
- sizeof(struct STRUCT) \
- }; \
- return &action; \
- }
-#include "ofp-util.def"
-
- case OFPAT10_VENDOR:
- default:
- return &action_bad_type;
- }
-}
-
-static const struct ofputil_action *
-ofputil_decode_nxast_action(const union ofp_action *a)
-{
- const struct nx_action_header *nah = (const struct nx_action_header *) a;
- enum nx_action_subtype subtype = ntohs(nah->subtype);
-
- switch (subtype) {
-#define NXAST_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME) \
- case ENUM: { \
- static const struct ofputil_action action = { \
- OFPUTIL_##ENUM, \
- sizeof(struct STRUCT), \
- EXTENSIBLE ? UINT_MAX : sizeof(struct STRUCT) \
- }; \
- return &action; \
- }
-#include "ofp-util.def"
-
- case NXAST_SNAT__OBSOLETE:
- case NXAST_DROP_SPOOFED_ARP__OBSOLETE:
- default:
- return &action_bad_type;
- }
-}
-
-/* Parses 'a' to determine its type. Returns a nonnegative OFPUTIL_OFPAT10_* or
- * OFPUTIL_NXAST_* constant if successful, otherwise a negative OFPERR_* error
- * code.
- *
- * The caller must have already verified that 'a''s length is correct (that is,
- * a->header.len is nonzero and a multiple of sizeof(union ofp_action) and no
- * longer than the amount of space allocated to 'a').
- *
- * This function verifies that 'a''s length is correct for the type of action
- * that it represents. */
-int
-ofputil_decode_action(const union ofp_action *a)
-{
- const struct ofputil_action *action;
- uint16_t len = ntohs(a->header.len);
-
- if (a->type != htons(OFPAT10_VENDOR)) {
- action = ofputil_decode_ofpat_action(a);
- } else {
- switch (ntohl(a->vendor.vendor)) {
- case NX_VENDOR_ID:
- if (len < sizeof(struct nx_action_header)) {
- return -OFPERR_OFPBAC_BAD_LEN;
- }
- action = ofputil_decode_nxast_action(a);
- break;
- default:
- action = &action_bad_vendor;
- break;
- }
- }
-
- return (len >= action->min_len && len <= action->max_len
- ? action->code
- : -OFPERR_OFPBAC_BAD_LEN);
-}
-
-/* Parses 'a' and returns its type as an OFPUTIL_OFPAT10_* or OFPUTIL_NXAST_*
- * constant. The caller must have already validated that 'a' is a valid action
- * understood by Open vSwitch (e.g. by a previous successful call to
- * ofputil_decode_action()). */
-enum ofputil_action_code
-ofputil_decode_action_unsafe(const union ofp_action *a)
-{
- const struct ofputil_action *action;
-
- if (a->type != htons(OFPAT10_VENDOR)) {
- action = ofputil_decode_ofpat_action(a);
- } else {
- action = ofputil_decode_nxast_action(a);
- }
-
- return action->code;
-}
-
/* Returns the 'enum ofputil_action_code' corresponding to 'name' (e.g. if
* 'name' is "output" then the return value is OFPUTIL_OFPAT10_OUTPUT), or -1 if
* 'name' is not the name of any action.
}
#include "ofp-util.def"
-/* Returns true if 'action' outputs to 'port', false otherwise. */
-bool
-action_outputs_to_port(const union ofp_action *action, ovs_be16 port)
-{
- switch (ofputil_decode_action(action)) {
- case OFPUTIL_OFPAT10_OUTPUT:
- return action->output.port == port;
- case OFPUTIL_OFPAT10_ENQUEUE:
- return ((const struct ofp_action_enqueue *) action)->port == port;
- case OFPUTIL_NXAST_CONTROLLER:
- return port == htons(OFPP_CONTROLLER);
- default:
- return false;
- }
-}
-
/* "Normalizes" the wildcards in 'rule'. That means:
*
* 1. If the type of level N is known, then only the valid fields for that
}
}
-/* Attempts to pull 'actions_len' bytes from the front of 'b'. Returns 0 if
- * successful, otherwise an OpenFlow error.
- *
- * If successful, the first action is stored in '*actionsp' and the number of
- * "union ofp_action" size elements into '*n_actionsp'. Otherwise NULL and 0
- * are stored, respectively.
- *
- * This function does not check that the actions are valid (the caller should
- * do so, with validate_actions()). The caller is also responsible for making
- * sure that 'b->data' is initially aligned appropriately for "union
- * ofp_action". */
-enum ofperr
-ofputil_pull_actions(struct ofpbuf *b, unsigned int actions_len,
- union ofp_action **actionsp, size_t *n_actionsp)
-{
- if (actions_len % OFP_ACTION_ALIGN != 0) {
- VLOG_WARN_RL(&bad_ofmsg_rl, "OpenFlow message actions length %u "
- "is not a multiple of %d", actions_len, OFP_ACTION_ALIGN);
- goto error;
- }
-
- *actionsp = ofpbuf_try_pull(b, actions_len);
- if (*actionsp == NULL) {
- VLOG_WARN_RL(&bad_ofmsg_rl, "OpenFlow message actions length %u "
- "exceeds remaining message length (%zu)",
- actions_len, b->size);
- goto error;
- }
-
- *n_actionsp = actions_len / OFP_ACTION_ALIGN;
- return 0;
-
-error:
- *actionsp = NULL;
- *n_actionsp = 0;
- return OFPERR_OFPBRC_BAD_LEN;
-}
-
-bool
-ofputil_actions_equal(const union ofp_action *a, size_t n_a,
- const union ofp_action *b, size_t n_b)
-{
- return n_a == n_b && (!n_a || !memcmp(a, b, n_a * sizeof *a));
-}
-
-union ofp_action *
-ofputil_actions_clone(const union ofp_action *actions, size_t n)
-{
- return n ? xmemdup(actions, n * sizeof *actions) : NULL;
-}
-
/* Parses a key or a key-value pair from '*stringp'.
*
* On success: Stores the key into '*keyp'. Stores the value, if present, into
#include <stddef.h>
#include <stdint.h>
#include "classifier.h"
+#include "compiler.h"
#include "flow.h"
#include "netdev.h"
#include "openflow/nicira-ext.h"
uint32_t buffer_id;
uint16_t out_port;
uint16_t flags;
- union ofp_action *actions;
- size_t n_actions;
+ struct ofpact *ofpacts; /* Series of "struct ofpact"s. */
+ size_t ofpacts_len; /* Length of ofpacts, in bytes. */
};
enum ofperr ofputil_decode_flow_mod(struct ofputil_flow_mod *,
const struct ofp_header *,
- enum ofputil_protocol);
+ enum ofputil_protocol,
+ struct ofpbuf *ofpacts);
struct ofpbuf *ofputil_encode_flow_mod(const struct ofputil_flow_mod *,
enum ofputil_protocol);
int hard_age; /* Seconds since last change, -1 if unknown. */
uint64_t packet_count; /* Packet count, UINT64_MAX if unknown. */
uint64_t byte_count; /* Byte count, UINT64_MAX if unknown. */
- union ofp_action *actions;
- size_t n_actions;
+ struct ofpact *ofpacts;
+ size_t ofpacts_len;
};
int ofputil_decode_flow_stats_reply(struct ofputil_flow_stats *,
struct ofpbuf *msg,
- bool flow_age_extension);
+ bool flow_age_extension,
+ struct ofpbuf *ofpacts);
void ofputil_append_flow_stats_reply(const struct ofputil_flow_stats *,
struct list *replies);
size_t packet_len; /* Length of packet data in bytes. */
uint32_t buffer_id; /* Buffer id or UINT32_MAX if no buffer. */
uint16_t in_port; /* Packet's input port. */
- union ofp_action *actions; /* Actions. */
- size_t n_actions; /* Number of elements in 'actions' array. */
+ struct ofpact *ofpacts; /* Actions. */
+ size_t ofpacts_len; /* Size of ofpacts in bytes. */
};
enum ofperr ofputil_decode_packet_out(struct ofputil_packet_out *,
- const struct ofp_packet_out *);
+ const struct ofp_packet_out *,
+ struct ofpbuf *ofpacts);
struct ofpbuf *ofputil_encode_packet_out(const struct ofputil_packet_out *);
enum ofputil_port_config {
struct list *);
struct ofpbuf *ofputil_reserve_stats_reply(size_t len, struct list *);
void *ofputil_append_stats_reply(size_t len, struct list *);
+void ofputil_postappend_stats_reply(size_t start_ofs, struct list *);
void ofputil_append_port_desc_stats_reply(uint8_t ofp_version,
const struct ofputil_phy_port *pp,
const void *ofputil_nxstats_body(const struct ofp_header *);
size_t ofputil_nxstats_body_len(const struct ofp_header *);
-struct ofpbuf *make_flow_mod(uint16_t command, const struct cls_rule *,
- size_t actions_len);
-struct ofpbuf *make_add_flow(const struct cls_rule *, uint32_t buffer_id,
- uint16_t max_idle, size_t actions_len);
-struct ofpbuf *make_packet_in(uint32_t buffer_id, uint16_t in_port,
- uint8_t reason,
- const struct ofpbuf *payload, int max_send_len);
+/* */
struct ofpbuf *make_echo_request(void);
struct ofpbuf *make_echo_reply(const struct ofp_header *rq);
*
* (The above list helps developers who want to "grep" for these definitions.)
*/
-enum ofputil_action_code {
+enum OVS_PACKED_ENUM ofputil_action_code {
OFPUTIL_ACTION_INVALID,
#define OFPAT10_ACTION(ENUM, STRUCT, NAME) OFPUTIL_##ENUM,
#define NXAST_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME) OFPUTIL_##ENUM,
#include "ofp-util.def"
};
-int ofputil_decode_action(const union ofp_action *);
-enum ofputil_action_code ofputil_decode_action_unsafe(
- const union ofp_action *);
-
int ofputil_action_code_from_name(const char *);
void *ofputil_put_action(enum ofputil_action_code, struct ofpbuf *buf);
#define OFP_ACTION_ALIGN 8 /* Alignment of ofp_actions. */
-static inline union ofp_action *
-ofputil_action_next(const union ofp_action *a)
-{
- return ((union ofp_action *) (void *)
- ((uint8_t *) a + ntohs(a->header.len)));
-}
-
-static inline bool
-ofputil_action_is_valid(const union ofp_action *a, size_t n_actions)
-{
- uint16_t len = ntohs(a->header.len);
- return (!(len % OFP_ACTION_ALIGN)
- && len >= sizeof *a
- && len / sizeof *a <= n_actions);
-}
-
-/* This macro is careful to check for actions with bad lengths. */
-#define OFPUTIL_ACTION_FOR_EACH(ITER, LEFT, ACTIONS, N_ACTIONS) \
- for ((ITER) = (ACTIONS), (LEFT) = (N_ACTIONS); \
- (LEFT) > 0 && ofputil_action_is_valid(ITER, LEFT); \
- ((LEFT) -= ntohs((ITER)->header.len) / sizeof(union ofp_action), \
- (ITER) = ofputil_action_next(ITER)))
-
-/* This macro does not check for actions with bad lengths. It should only be
- * used with actions from trusted sources or with actions that have already
- * been validated (e.g. with OFPUTIL_ACTION_FOR_EACH). */
-#define OFPUTIL_ACTION_FOR_EACH_UNSAFE(ITER, LEFT, ACTIONS, N_ACTIONS) \
- for ((ITER) = (ACTIONS), (LEFT) = (N_ACTIONS); \
- (LEFT) > 0; \
- ((LEFT) -= ntohs((ITER)->header.len) / sizeof(union ofp_action), \
- (ITER) = ofputil_action_next(ITER)))
-
enum ofperr validate_actions(const union ofp_action *, size_t n_actions,
const struct flow *, int max_ports);
bool action_outputs_to_port(const union ofp_action *, ovs_be16 port);
#include "fail-open.h"
#include "in-band.h"
#include "odp-util.h"
+#include "ofp-actions.h"
#include "ofp-util.h"
#include "ofpbuf.h"
#include "ofproto-provider.h"
* traffic until a controller has been defined and it tells us to do so. */
if (!connmgr_has_controllers(mgr)
&& mgr->fail_mode == OFPROTO_FAIL_STANDALONE) {
- union ofp_action action;
+ struct ofpbuf ofpacts;
struct cls_rule rule;
- memset(&action, 0, sizeof action);
- action.type = htons(OFPAT10_OUTPUT);
- action.output.len = htons(sizeof action);
- action.output.port = htons(OFPP_NORMAL);
+ ofpbuf_init(&ofpacts, OFPACT_OUTPUT_SIZE);
+ ofpact_put_OUTPUT(&ofpacts)->port = OFPP_NORMAL;
+ ofpact_pad(&ofpacts);
+
cls_rule_init_catchall(&rule, 0);
- ofproto_add_flow(mgr->ofproto, &rule, &action, 1);
+ ofproto_add_flow(mgr->ofproto, &rule, ofpacts.data, ofpacts.size);
+
+ ofpbuf_uninit(&ofpacts);
}
}
\f
#include "flow.h"
#include "mac-learning.h"
#include "odp-util.h"
+#include "ofp-actions.h"
#include "ofp-util.h"
#include "ofpbuf.h"
#include "ofproto.h"
int disconn_secs = connmgr_failure_duration(fo->connmgr);
bool open = disconn_secs >= trigger_duration(fo);
if (open) {
- union ofp_action action;
+ struct ofpbuf ofpacts;
struct cls_rule rule;
/* Set up a flow that matches every packet and directs them to
* OFPP_NORMAL. */
- memset(&action, 0, sizeof action);
- action.type = htons(OFPAT10_OUTPUT);
- action.output.len = htons(sizeof action);
- action.output.port = htons(OFPP_NORMAL);
+ ofpbuf_init(&ofpacts, OFPACT_OUTPUT_SIZE);
+ ofpact_put_OUTPUT(&ofpacts)->port = OFPP_NORMAL;
+ ofpact_pad(&ofpacts);
cls_rule_init_catchall(&rule, FAIL_OPEN_PRIORITY);
- ofproto_add_flow(fo->ofproto, &rule, &action, 1);
+ ofproto_add_flow(fo->ofproto, &rule, ofpacts.data, ofpacts.size);
+
+ ofpbuf_uninit(&ofpacts);
}
}
/*
- * Copyright (c) 2008, 2009, 2010, 2011 Nicira, Inc.
+ * 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.
#include "netdev.h"
#include "netlink.h"
#include "odp-util.h"
+#include "ofp-actions.h"
#include "ofproto.h"
#include "ofpbuf.h"
#include "ofproto-provider.h"
bool
in_band_run(struct in_band *ib)
{
- struct {
- struct nx_action_set_queue nxsq;
- union ofp_action oa;
- } actions;
- const void *a;
- size_t na;
+ uint64_t ofpacts_stub[128 / 8];
+ struct ofpbuf ofpacts;
struct in_band_rule *rule, *next;
- memset(&actions, 0, sizeof actions);
- actions.oa.output.type = htons(OFPAT10_OUTPUT);
- actions.oa.output.len = htons(sizeof actions.oa);
- actions.oa.output.port = htons(OFPP_NORMAL);
- actions.oa.output.max_len = htons(0);
- if (ib->queue_id < 0) {
- a = &actions.oa;
- na = sizeof actions.oa / sizeof(union ofp_action);
- } else {
- actions.nxsq.type = htons(OFPAT10_VENDOR);
- actions.nxsq.len = htons(sizeof actions.nxsq);
- actions.nxsq.vendor = htonl(NX_VENDOR_ID);
- actions.nxsq.subtype = htons(NXAST_SET_QUEUE);
- actions.nxsq.queue_id = htonl(ib->queue_id);
- a = &actions;
- na = sizeof actions / sizeof(union ofp_action);
+ ofpbuf_use_stub(&ofpacts, ofpacts_stub, sizeof ofpacts_stub);
+
+ if (ib->queue_id >= 0) {
+ ofpact_put_SET_QUEUE(&ofpacts)->queue_id = ib->queue_id;
}
+ ofpact_put_OUTPUT(&ofpacts)->port = OFPP_NORMAL;
refresh_local(ib);
refresh_remotes(ib);
HMAP_FOR_EACH_SAFE (rule, next, cls_rule.hmap_node, &ib->rules) {
switch (rule->op) {
case ADD:
- ofproto_add_flow(ib->ofproto, &rule->cls_rule, a, na);
+ ofproto_add_flow(ib->ofproto, &rule->cls_rule,
+ ofpacts.data, ofpacts.size);
break;
case DELETE:
}
}
+ ofpbuf_uninit(&ofpacts);
+
return ib->n_remotes || !hmap_is_empty(&ib->rules);
}
#include "odp-util.h"
#include "ofp-util.h"
#include "ofpbuf.h"
+#include "ofp-actions.h"
#include "ofp-parse.h"
#include "ofp-print.h"
#include "ofproto-dpif-governor.h"
ovs_be16 initial_tci, struct rule_dpif *,
uint8_t tcp_flags, const struct ofpbuf *);
static void xlate_actions(struct action_xlate_ctx *,
- const union ofp_action *in, size_t n_in,
+ const struct ofpact *ofpacts, size_t ofpacts_len,
struct ofpbuf *odp_actions);
static void xlate_actions_for_side_effects(struct action_xlate_ctx *,
- const union ofp_action *in,
- size_t n_in);
+ const struct ofpact *ofpacts,
+ size_t ofpacts_len);
static size_t put_userspace_action(const struct ofproto_dpif *,
struct ofpbuf *odp_actions,
static int
add_internal_flow(struct ofproto_dpif *ofproto, int id,
- const struct ofpbuf *actions, struct rule_dpif **rulep)
+ const struct ofpbuf *ofpacts, struct rule_dpif **rulep)
{
struct ofputil_flow_mod fm;
int error;
fm.buffer_id = 0;
fm.out_port = 0;
fm.flags = 0;
- fm.actions = actions->data;
- fm.n_actions = actions->size / sizeof(union ofp_action);
+ fm.ofpacts = ofpacts->data;
+ fm.ofpacts_len = ofpacts->size;
error = ofproto_flow_mod(&ofproto->up, &fm);
if (error) {
static int
add_internal_flows(struct ofproto_dpif *ofproto)
{
- struct nx_action_controller *nac;
- uint64_t actions_stub[128 / 8];
- struct ofpbuf actions;
+ struct ofpact_controller *controller;
+ uint64_t ofpacts_stub[128 / 8];
+ struct ofpbuf ofpacts;
int error;
int id;
- ofpbuf_use_stack(&actions, actions_stub, sizeof actions_stub);
+ ofpbuf_use_stack(&ofpacts, ofpacts_stub, sizeof ofpacts_stub);
id = 1;
- nac = ofputil_put_NXAST_CONTROLLER(&actions);
- nac->max_len = htons(UINT16_MAX);
- nac->controller_id = htons(0);
- nac->reason = OFPR_NO_MATCH;
- error = add_internal_flow(ofproto, id++, &actions, &ofproto->miss_rule);
+ controller = ofpact_put_CONTROLLER(&ofpacts);
+ controller->max_len = UINT16_MAX;
+ controller->controller_id = 0;
+ controller->reason = OFPR_NO_MATCH;
+ ofpact_pad(&ofpacts);
+
+ error = add_internal_flow(ofproto, id++, &ofpacts, &ofproto->miss_rule);
if (error) {
return error;
}
- ofpbuf_clear(&actions);
- error = add_internal_flow(ofproto, id++, &actions,
+ ofpbuf_clear(&ofpacts);
+ error = add_internal_flow(ofproto, id++, &ofpacts,
&ofproto->no_packet_in_rule);
return error;
}
action_xlate_ctx_init(&ctx, ofproto, &miss->flow, miss->initial_tci,
rule, 0, packet);
ctx.resubmit_stats = &stats;
- xlate_actions(&ctx, rule->up.actions, rule->up.n_actions,
+ xlate_actions(&ctx, rule->up.ofpacts, rule->up.ofpacts_len,
&odp_actions);
if (odp_actions.size) {
facet->flow.vlan_tci,
facet->rule, facet->tcp_flags, NULL);
ctx.may_learn = true;
- xlate_actions_for_side_effects(&ctx, facet->rule->up.actions,
- facet->rule->up.n_actions);
+ xlate_actions_for_side_effects(&ctx, facet->rule->up.ofpacts,
+ facet->rule->up.ofpacts_len);
}
static void
static bool
facet_is_controller_flow(struct facet *facet)
{
- return (facet
- && facet->rule->up.n_actions == 1
- && action_outputs_to_port(&facet->rule->up.actions[0],
- htons(OFPP_CONTROLLER)));
+ if (facet) {
+ const struct rule *rule = &facet->rule->up;
+ const struct ofpact *ofpacts = rule->ofpacts;
+ size_t ofpacts_len = rule->ofpacts_len;
+
+ if (ofpacts->type == OFPACT_CONTROLLER &&
+ ofpact_next(ofpacts) >= ofpact_end(ofpacts, ofpacts_len)) {
+ return true;
+ }
+ }
+ return false;
}
/* Folds all of 'facet''s statistics into its rule. Also updates the
action_xlate_ctx_init(&ctx, ofproto, &facet->flow,
subfacet->initial_tci, rule, 0, NULL);
- xlate_actions(&ctx, rule->up.actions, rule->up.n_actions,
+ xlate_actions(&ctx, rule->up.ofpacts, rule->up.ofpacts_len,
&odp_actions);
if (subfacet->path == SF_NOT_INSTALLED) {
action_xlate_ctx_init(&ctx, ofproto, &facet->flow,
subfacet->initial_tci, new_rule, 0, NULL);
- xlate_actions(&ctx, new_rule->up.actions, new_rule->up.n_actions,
+ xlate_actions(&ctx, new_rule->up.ofpacts, new_rule->up.ofpacts_len,
&odp_actions);
slow = (subfacet->slow & SLOW_MATCH) | ctx.slow;
action_xlate_ctx_init(&ctx, ofproto, flow, flow->vlan_tci, rule,
0, NULL);
ctx.resubmit_stats = stats;
- xlate_actions_for_side_effects(&ctx, rule->up.actions, rule->up.n_actions);
+ xlate_actions_for_side_effects(&ctx, rule->up.ofpacts,
+ rule->up.ofpacts_len);
}
\f
/* Subfacets. */
action_xlate_ctx_init(&ctx, ofproto, &facet->flow, subfacet->initial_tci,
rule, 0, packet);
- xlate_actions(&ctx, rule->up.actions, rule->up.n_actions, odp_actions);
+ xlate_actions(&ctx, rule->up.ofpacts, rule->up.ofpacts_len, odp_actions);
facet->tags = ctx.tags;
facet->has_learn = ctx.has_learn;
facet->has_normal = ctx.has_normal;
uint8_t table_id;
enum ofperr error;
- error = validate_actions(rule->up.actions, rule->up.n_actions,
- &rule->up.cr.flow, ofproto->max_ports);
+ error = ofpacts_check(rule->up.ofpacts, rule->up.ofpacts_len,
+ &rule->up.cr.flow, ofproto->max_ports);
if (error) {
return error;
}
action_xlate_ctx_init(&ctx, ofproto, flow, flow->vlan_tci,
rule, stats.tcp_flags, packet);
ctx.resubmit_stats = &stats;
- xlate_actions(&ctx, rule->up.actions, rule->up.n_actions, &odp_actions);
+ xlate_actions(&ctx, rule->up.ofpacts, rule->up.ofpacts_len, &odp_actions);
execute_odp_actions(ofproto, flow, odp_actions.data,
odp_actions.size, packet);
struct ofproto_dpif *ofproto = ofproto_dpif_cast(rule->up.ofproto);
enum ofperr error;
- error = validate_actions(rule->up.actions, rule->up.n_actions,
- &rule->up.cr.flow, ofproto->max_ports);
+ error = ofpacts_check(rule->up.ofpacts, rule->up.ofpacts_len,
+ &rule->up.cr.flow, ofproto->max_ports);
if (error) {
ofoperation_complete(rule->up.pending, error);
return;
\f
/* OpenFlow to datapath action translation. */
-static void do_xlate_actions(const union ofp_action *in, size_t n_in,
- struct action_xlate_ctx *ctx);
+static void do_xlate_actions(const struct ofpact *, size_t ofpacts_len,
+ struct action_xlate_ctx *);
static void xlate_normal(struct action_xlate_ctx *);
/* Composes an ODP action for a "slow path" action for 'flow' within 'ofproto'.
ctx->recurse++;
ctx->rule = rule;
- do_xlate_actions(rule->up.actions, rule->up.n_actions, ctx);
+ do_xlate_actions(rule->up.ofpacts, rule->up.ofpacts_len, ctx);
ctx->rule = old_rule;
ctx->recurse--;
}
}
static void
-xlate_resubmit_table(struct action_xlate_ctx *ctx,
- const struct nx_action_resubmit *nar)
+xlate_ofpact_resubmit(struct action_xlate_ctx *ctx,
+ const struct ofpact_resubmit *resubmit)
{
uint16_t in_port;
uint8_t table_id;
- in_port = (nar->in_port == htons(OFPP_IN_PORT)
- ? ctx->flow.in_port
- : ntohs(nar->in_port));
- table_id = nar->table == 255 ? ctx->table_id : nar->table;
+ in_port = resubmit->in_port;
+ if (in_port == OFPP_IN_PORT) {
+ in_port = ctx->flow.in_port;
+ }
+
+ table_id = resubmit->table_id;
+ if (table_id == 255) {
+ table_id = ctx->table_id;
+ }
xlate_table_action(ctx, in_port, table_id);
}
}
static void
-xlate_output_action__(struct action_xlate_ctx *ctx,
- uint16_t port, uint16_t max_len)
+xlate_output_action(struct action_xlate_ctx *ctx,
+ uint16_t port, uint16_t max_len)
{
uint16_t prev_nf_output_iface = ctx->nf_output_iface;
static void
xlate_output_reg_action(struct action_xlate_ctx *ctx,
- const struct nx_action_output_reg *naor)
+ const struct ofpact_output_reg *or)
{
- struct mf_subfield src;
- uint64_t ofp_port;
-
- nxm_decode(&src, naor->src, naor->ofs_nbits);
- ofp_port = mf_get_subfield(&src, &ctx->flow);
-
- if (ofp_port <= UINT16_MAX) {
- xlate_output_action__(ctx, ofp_port, ntohs(naor->max_len));
+ uint64_t port = mf_get_subfield(&or->src, &ctx->flow);
+ if (port <= UINT16_MAX) {
+ xlate_output_action(ctx, port, or->max_len);
}
}
-static void
-xlate_output_action(struct action_xlate_ctx *ctx,
- const struct ofp_action_output *oao)
-{
- xlate_output_action__(ctx, ntohs(oao->port), ntohs(oao->max_len));
-}
-
static void
xlate_enqueue_action(struct action_xlate_ctx *ctx,
- const struct ofp_action_enqueue *oae)
+ const struct ofpact_enqueue *enqueue)
{
- uint16_t ofp_port;
+ uint16_t ofp_port = enqueue->port;
+ uint32_t queue_id = enqueue->queue;
uint32_t flow_priority, priority;
int error;
- error = dpif_queue_to_priority(ctx->ofproto->dpif, ntohl(oae->queue_id),
- &priority);
+ /* Translate queue to priority. */
+ error = dpif_queue_to_priority(ctx->ofproto->dpif, queue_id, &priority);
if (error) {
/* Fall back to ordinary output action. */
- xlate_output_action__(ctx, ntohs(oae->port), 0);
+ xlate_output_action(ctx, enqueue->port, 0);
return;
}
- /* Figure out datapath output port. */
- ofp_port = ntohs(oae->port);
+ /* Check output port. */
if (ofp_port == OFPP_IN_PORT) {
ofp_port = ctx->flow.in_port;
} else if (ofp_port == ctx->flow.in_port) {
}
static void
-xlate_set_queue_action(struct action_xlate_ctx *ctx,
- const struct nx_action_set_queue *nasq)
+xlate_set_queue_action(struct action_xlate_ctx *ctx, uint32_t queue_id)
{
- uint32_t priority;
- int error;
+ uint32_t skb_priority;
- error = dpif_queue_to_priority(ctx->ofproto->dpif, ntohl(nasq->queue_id),
- &priority);
- if (error) {
- /* Couldn't translate queue to a priority, so ignore. A warning
+ if (!dpif_queue_to_priority(ctx->ofproto->dpif, queue_id, &skb_priority)) {
+ ctx->flow.skb_priority = skb_priority;
+ } else {
+ /* Couldn't translate queue to a priority. Nothing to do. A warning
* has already been logged. */
- return;
}
-
- ctx->flow.skb_priority = priority;
}
struct xlate_reg_state {
static void
xlate_autopath(struct action_xlate_ctx *ctx,
- const struct nx_action_autopath *naa)
+ const struct ofpact_autopath *ap)
{
- uint16_t ofp_port = ntohl(naa->id);
+ uint16_t ofp_port = ap->port;
struct ofport_dpif *port = get_ofp_port(ctx->ofproto, ofp_port);
if (!port || !port->bundle) {
ofp_port = slave->up.ofp_port;
}
}
- autopath_execute(naa, &ctx->flow, ofp_port);
+ nxm_reg_load(&ap->dst, ofp_port, &ctx->flow);
}
static bool
}
}
+static void
+xlate_bundle_action(struct action_xlate_ctx *ctx,
+ const struct ofpact_bundle *bundle)
+{
+ uint16_t port;
+
+ port = bundle_execute(bundle, &ctx->flow, slave_enabled_cb, ctx->ofproto);
+ if (bundle->dst.field) {
+ nxm_reg_load(&bundle->dst, port, &ctx->flow);
+ } else {
+ xlate_output_action(ctx, port, 0);
+ }
+}
+
static void
xlate_learn_action(struct action_xlate_ctx *ctx,
- const struct nx_action_learn *learn)
+ const struct ofpact_learn *learn)
{
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
struct ofputil_flow_mod fm;
+ uint64_t ofpacts_stub[1024 / 8];
+ struct ofpbuf ofpacts;
int error;
- learn_execute(learn, &ctx->flow, &fm);
+ ofpbuf_use_stack(&ofpacts, ofpacts_stub, sizeof ofpacts_stub);
+ learn_execute(learn, &ctx->flow, &fm, &ofpacts);
error = ofproto_flow_mod(&ctx->ofproto->up, &fm);
if (error && !VLOG_DROP_WARN(&rl)) {
ofperr_get_name(error));
}
- free(fm.actions);
+ ofpbuf_uninit(&ofpacts);
}
/* Reduces '*timeout' to no more than 'max'. A value of zero in either case
static void
xlate_fin_timeout(struct action_xlate_ctx *ctx,
- const struct nx_action_fin_timeout *naft)
+ const struct ofpact_fin_timeout *oft)
{
if (ctx->tcp_flags & (TCP_FIN | TCP_RST) && ctx->rule) {
struct rule_dpif *rule = ctx->rule;
- reduce_timeout(ntohs(naft->fin_idle_timeout), &rule->up.idle_timeout);
- reduce_timeout(ntohs(naft->fin_hard_timeout), &rule->up.hard_timeout);
+ reduce_timeout(oft->fin_idle_timeout, &rule->up.idle_timeout);
+ reduce_timeout(oft->fin_hard_timeout, &rule->up.hard_timeout);
}
}
}
static void
-do_xlate_actions(const union ofp_action *in, size_t n_in,
+do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
struct action_xlate_ctx *ctx)
{
const struct ofport_dpif *port;
- const union ofp_action *ia;
bool was_evictable = true;
- size_t left;
+ const struct ofpact *a;
port = get_ofp_port(ctx->ofproto, ctx->flow.in_port);
if (port && !may_receive(port, ctx)) {
was_evictable = ctx->rule->up.evictable;
ctx->rule->up.evictable = false;
}
- OFPUTIL_ACTION_FOR_EACH_UNSAFE (ia, left, in, n_in) {
- const struct ofp_action_dl_addr *oada;
- const struct nx_action_resubmit *nar;
- const struct nx_action_set_tunnel *nast;
- const struct nx_action_set_queue *nasq;
- const struct nx_action_multipath *nam;
- const struct nx_action_autopath *naa;
- const struct nx_action_bundle *nab;
- const struct nx_action_output_reg *naor;
- const struct nx_action_controller *nac;
- enum ofputil_action_code code;
- ovs_be64 tun_id;
+ OFPACT_FOR_EACH (a, ofpacts, ofpacts_len) {
+ struct ofpact_controller *controller;
if (ctx->exit) {
break;
}
- code = ofputil_decode_action_unsafe(ia);
- switch (code) {
- case OFPUTIL_ACTION_INVALID:
- NOT_REACHED();
+ switch (a->type) {
+ case OFPACT_OUTPUT:
+ xlate_output_action(ctx, ofpact_get_OUTPUT(a)->port,
+ ofpact_get_OUTPUT(a)->max_len);
+ break;
+
+ case OFPACT_CONTROLLER:
+ controller = ofpact_get_CONTROLLER(a);
+ execute_controller_action(ctx, controller->max_len,
+ controller->reason,
+ controller->controller_id);
+ break;
- case OFPUTIL_OFPAT10_OUTPUT:
- xlate_output_action(ctx, &ia->output);
+ case OFPACT_ENQUEUE:
+ xlate_enqueue_action(ctx, ofpact_get_ENQUEUE(a));
break;
- case OFPUTIL_OFPAT10_SET_VLAN_VID:
+ case OFPACT_SET_VLAN_VID:
ctx->flow.vlan_tci &= ~htons(VLAN_VID_MASK);
- ctx->flow.vlan_tci |= ia->vlan_vid.vlan_vid | htons(VLAN_CFI);
+ ctx->flow.vlan_tci |= (htons(ofpact_get_SET_VLAN_VID(a)->vlan_vid)
+ | htons(VLAN_CFI));
break;
- case OFPUTIL_OFPAT10_SET_VLAN_PCP:
+ case OFPACT_SET_VLAN_PCP:
ctx->flow.vlan_tci &= ~htons(VLAN_PCP_MASK);
- ctx->flow.vlan_tci |= htons(
- (ia->vlan_pcp.vlan_pcp << VLAN_PCP_SHIFT) | VLAN_CFI);
+ ctx->flow.vlan_tci |= htons((ofpact_get_SET_VLAN_PCP(a)->vlan_pcp
+ << VLAN_PCP_SHIFT)
+ | VLAN_CFI);
break;
- case OFPUTIL_OFPAT10_STRIP_VLAN:
+ case OFPACT_STRIP_VLAN:
ctx->flow.vlan_tci = htons(0);
break;
- case OFPUTIL_OFPAT10_SET_DL_SRC:
- oada = ((struct ofp_action_dl_addr *) ia);
- memcpy(ctx->flow.dl_src, oada->dl_addr, ETH_ADDR_LEN);
+ case OFPACT_SET_ETH_SRC:
+ memcpy(ctx->flow.dl_src, ofpact_get_SET_ETH_SRC(a)->mac,
+ ETH_ADDR_LEN);
break;
- case OFPUTIL_OFPAT10_SET_DL_DST:
- oada = ((struct ofp_action_dl_addr *) ia);
- memcpy(ctx->flow.dl_dst, oada->dl_addr, ETH_ADDR_LEN);
+ case OFPACT_SET_ETH_DST:
+ memcpy(ctx->flow.dl_dst, ofpact_get_SET_ETH_DST(a)->mac,
+ ETH_ADDR_LEN);
break;
- case OFPUTIL_OFPAT10_SET_NW_SRC:
- ctx->flow.nw_src = ia->nw_addr.nw_addr;
+ case OFPACT_SET_IPV4_SRC:
+ ctx->flow.nw_src = ofpact_get_SET_IPV4_SRC(a)->ipv4;
break;
- case OFPUTIL_OFPAT10_SET_NW_DST:
- ctx->flow.nw_dst = ia->nw_addr.nw_addr;
+ case OFPACT_SET_IPV4_DST:
+ ctx->flow.nw_dst = ofpact_get_SET_IPV4_DST(a)->ipv4;
break;
- case OFPUTIL_OFPAT10_SET_NW_TOS:
+ case OFPACT_SET_IPV4_DSCP:
/* OpenFlow 1.0 only supports IPv4. */
if (ctx->flow.dl_type == htons(ETH_TYPE_IP)) {
ctx->flow.nw_tos &= ~IP_DSCP_MASK;
- ctx->flow.nw_tos |= ia->nw_tos.nw_tos & IP_DSCP_MASK;
+ ctx->flow.nw_tos |= ofpact_get_SET_IPV4_DSCP(a)->dscp;
}
break;
- case OFPUTIL_OFPAT10_SET_TP_SRC:
- ctx->flow.tp_src = ia->tp_port.tp_port;
+ case OFPACT_SET_L4_SRC_PORT:
+ ctx->flow.tp_src = htons(ofpact_get_SET_L4_SRC_PORT(a)->port);
break;
- case OFPUTIL_OFPAT10_SET_TP_DST:
- ctx->flow.tp_dst = ia->tp_port.tp_port;
+ case OFPACT_SET_L4_DST_PORT:
+ ctx->flow.tp_dst = htons(ofpact_get_SET_L4_DST_PORT(a)->port);
break;
- case OFPUTIL_OFPAT10_ENQUEUE:
- xlate_enqueue_action(ctx, (const struct ofp_action_enqueue *) ia);
+ case OFPACT_RESUBMIT:
+ xlate_ofpact_resubmit(ctx, ofpact_get_RESUBMIT(a));
break;
- case OFPUTIL_NXAST_RESUBMIT:
- nar = (const struct nx_action_resubmit *) ia;
- xlate_table_action(ctx, ntohs(nar->in_port), ctx->table_id);
+ case OFPACT_SET_TUNNEL:
+ ctx->flow.tun_id = htonll(ofpact_get_SET_TUNNEL(a)->tun_id);
break;
- case OFPUTIL_NXAST_RESUBMIT_TABLE:
- xlate_resubmit_table(ctx, (const struct nx_action_resubmit *) ia);
+ case OFPACT_SET_QUEUE:
+ xlate_set_queue_action(ctx, ofpact_get_SET_QUEUE(a)->queue_id);
break;
- case OFPUTIL_NXAST_SET_TUNNEL:
- nast = (const struct nx_action_set_tunnel *) ia;
- tun_id = htonll(ntohl(nast->tun_id));
- ctx->flow.tun_id = tun_id;
- break;
-
- case OFPUTIL_NXAST_SET_QUEUE:
- nasq = (const struct nx_action_set_queue *) ia;
- xlate_set_queue_action(ctx, nasq);
- break;
-
- case OFPUTIL_NXAST_POP_QUEUE:
+ case OFPACT_POP_QUEUE:
ctx->flow.skb_priority = ctx->orig_skb_priority;
break;
- case OFPUTIL_NXAST_REG_MOVE:
- nxm_execute_reg_move((const struct nx_action_reg_move *) ia,
- &ctx->flow);
- break;
-
- case OFPUTIL_NXAST_REG_LOAD:
- nxm_execute_reg_load((const struct nx_action_reg_load *) ia,
- &ctx->flow);
+ case OFPACT_REG_MOVE:
+ nxm_execute_reg_move(ofpact_get_REG_MOVE(a), &ctx->flow);
break;
- case OFPUTIL_NXAST_NOTE:
- /* Nothing to do. */
+ case OFPACT_REG_LOAD:
+ nxm_execute_reg_load(ofpact_get_REG_LOAD(a), &ctx->flow);
break;
- case OFPUTIL_NXAST_SET_TUNNEL64:
- tun_id = ((const struct nx_action_set_tunnel64 *) ia)->tun_id;
- ctx->flow.tun_id = tun_id;
+ case OFPACT_DEC_TTL:
+ if (compose_dec_ttl(ctx)) {
+ goto out;
+ }
break;
- case OFPUTIL_NXAST_MULTIPATH:
- nam = (const struct nx_action_multipath *) ia;
- multipath_execute(nam, &ctx->flow);
+ case OFPACT_NOTE:
+ /* Nothing to do. */
break;
- case OFPUTIL_NXAST_AUTOPATH:
- naa = (const struct nx_action_autopath *) ia;
- xlate_autopath(ctx, naa);
+ case OFPACT_MULTIPATH:
+ multipath_execute(ofpact_get_MULTIPATH(a), &ctx->flow);
break;
- case OFPUTIL_NXAST_BUNDLE:
- ctx->ofproto->has_bundle_action = true;
- nab = (const struct nx_action_bundle *) ia;
- xlate_output_action__(ctx, bundle_execute(nab, &ctx->flow,
- slave_enabled_cb,
- ctx->ofproto), 0);
+ case OFPACT_AUTOPATH:
+ xlate_autopath(ctx, ofpact_get_AUTOPATH(a));
break;
- case OFPUTIL_NXAST_BUNDLE_LOAD:
+ case OFPACT_BUNDLE:
ctx->ofproto->has_bundle_action = true;
- nab = (const struct nx_action_bundle *) ia;
- bundle_execute_load(nab, &ctx->flow, slave_enabled_cb,
- ctx->ofproto);
+ xlate_bundle_action(ctx, ofpact_get_BUNDLE(a));
break;
- case OFPUTIL_NXAST_OUTPUT_REG:
- naor = (const struct nx_action_output_reg *) ia;
- xlate_output_reg_action(ctx, naor);
+ case OFPACT_OUTPUT_REG:
+ xlate_output_reg_action(ctx, ofpact_get_OUTPUT_REG(a));
break;
- case OFPUTIL_NXAST_LEARN:
+ case OFPACT_LEARN:
ctx->has_learn = true;
if (ctx->may_learn) {
- xlate_learn_action(ctx, (const struct nx_action_learn *) ia);
+ xlate_learn_action(ctx, ofpact_get_LEARN(a));
}
break;
- case OFPUTIL_NXAST_DEC_TTL:
- if (compose_dec_ttl(ctx)) {
- goto out;
- }
- break;
-
- case OFPUTIL_NXAST_EXIT:
+ case OFPACT_EXIT:
ctx->exit = true;
break;
- case OFPUTIL_NXAST_FIN_TIMEOUT:
+ case OFPACT_FIN_TIMEOUT:
ctx->has_fin_timeout = true;
- xlate_fin_timeout(ctx, (const struct nx_action_fin_timeout *) ia);
- break;
-
- case OFPUTIL_NXAST_CONTROLLER:
- nac = (const struct nx_action_controller *) ia;
- execute_controller_action(ctx, ntohs(nac->max_len), nac->reason,
- ntohs(nac->controller_id));
+ xlate_fin_timeout(ctx, ofpact_get_FIN_TIMEOUT(a));
break;
}
}
ctx->resubmit_stats = NULL;
}
-/* Translates the 'n_in' "union ofp_action"s in 'in' into datapath actions in
- * 'odp_actions', using 'ctx'. */
+/* Translates the 'ofpacts_len' bytes of "struct ofpacts" starting at 'ofpacts'
+ * into datapath actions in 'odp_actions', using 'ctx'. */
static void
xlate_actions(struct action_xlate_ctx *ctx,
- const union ofp_action *in, size_t n_in,
+ const struct ofpact *ofpacts, size_t ofpacts_len,
struct ofpbuf *odp_actions)
{
/* Normally false. Set to true if we ever hit MAX_RESUBMIT_RECURSION, so
ovs_be16 initial_tci = ctx->base_flow.vlan_tci;
add_sflow_action(ctx);
- do_xlate_actions(in, n_in, ctx);
+ do_xlate_actions(ofpacts, ofpacts_len, ctx);
if (ctx->max_resubmit_trigger && !ctx->resubmit_hook) {
if (!hit_resubmit_limit) {
}
}
-/* Translates the 'n_in' "union ofp_action"s in 'in' into datapath actions,
- * using 'ctx', and discards the datapath actions. */
+/* Translates the 'ofpacts_len' bytes of "struct ofpact"s starting at 'ofpacts'
+ * into datapath actions, using 'ctx', and discards the datapath actions. */
static void
xlate_actions_for_side_effects(struct action_xlate_ctx *ctx,
- const union ofp_action *in, size_t n_in)
+ const struct ofpact *ofpacts,
+ size_t ofpacts_len)
{
uint64_t odp_actions_stub[1024 / 8];
struct ofpbuf odp_actions;
ofpbuf_use_stub(&odp_actions, odp_actions_stub, sizeof odp_actions_stub);
- xlate_actions(ctx, in, n_in, &odp_actions);
+ xlate_actions(ctx, ofpacts, ofpacts_len, &odp_actions);
ofpbuf_uninit(&odp_actions);
}
\f
static enum ofperr
packet_out(struct ofproto *ofproto_, struct ofpbuf *packet,
const struct flow *flow,
- const union ofp_action *ofp_actions, size_t n_ofp_actions)
+ const struct ofpact *ofpacts, size_t ofpacts_len)
{
struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
enum ofperr error;
return OFPERR_NXBRC_BAD_IN_PORT;
}
- error = validate_actions(ofp_actions, n_ofp_actions, flow,
- ofproto->max_ports);
+ error = ofpacts_check(ofpacts, ofpacts_len, flow, ofproto->max_ports);
if (!error) {
struct odputil_keybuf keybuf;
struct dpif_flow_stats stats;
ofpbuf_use_stub(&odp_actions,
odp_actions_stub, sizeof odp_actions_stub);
- xlate_actions(&ctx, ofp_actions, n_ofp_actions, &odp_actions);
+ xlate_actions(&ctx, ofpacts, ofpacts_len, &odp_actions);
dpif_execute(ofproto->dpif, key.data, key.size,
odp_actions.data, odp_actions.size, packet);
ofpbuf_uninit(&odp_actions);
ds_put_char_multiple(result, '\t', level);
ds_put_cstr(result, "OpenFlow ");
- ofp_print_actions(result, rule->up.actions, rule->up.n_actions);
+ ofpacts_format(rule->up.ofpacts, rule->up.ofpacts_len, result);
ds_put_char(result, '\n');
}
action_xlate_ctx_init(&trace.ctx, ofproto, flow, initial_tci,
rule, tcp_flags, packet);
trace.ctx.resubmit_hook = trace_resubmit;
- xlate_actions(&trace.ctx, rule->up.actions, rule->up.n_actions,
+ xlate_actions(&trace.ctx, rule->up.ofpacts, rule->up.ofpacts_len,
&odp_actions);
ds_put_char(ds, '\n');
#include "shash.h"
#include "timeval.h"
+struct ofpact;
struct ofputil_flow_mod;
struct simap;
struct heap_node evg_node; /* In eviction_group's "rules" heap. */
struct eviction_group *eviction_group; /* NULL if not in any group. */
- union ofp_action *actions; /* OpenFlow actions. */
- int n_actions; /* Number of elements in actions[]. */
+ struct ofpact *ofpacts; /* Sequence of "struct ofpacts". */
+ unsigned int ofpacts_len; /* Size of 'ofpacts', in bytes. */
};
static inline struct rule *
* registers, then it is an error if 'rule->cr' does not wildcard all
* registers.
*
- * - Validate that 'rule->actions' and 'rule->n_actions' are well-formed
- * OpenFlow actions that the datapath can correctly implement. The
- * validate_actions() function (in ofp-util.c) can be useful as a model
- * for action validation, but it accepts all of the OpenFlow actions
- * that OVS understands. If your ofproto implementation only
- * implements a subset of those, then you should implement your own
- * action validation.
+ * - Validate that 'rule->ofpacts' is a sequence of well-formed actions
+ * that the datapath can correctly implement. If your ofproto
+ * implementation only implements a subset of the actions that Open
+ * vSwitch understands, then you should implement your own action
+ * validation.
*
* - If the rule is valid, update the datapath flow table, adding the new
* rule or replacing the existing one.
enum ofp_config_flags frag_handling);
/* Implements the OpenFlow OFPT_PACKET_OUT command. The datapath should
- * execute the 'n_actions' in the 'actions' array on 'packet'.
+ * execute the 'ofpacts_len' bytes of "struct ofpacts" in 'ofpacts'.
*
- * The caller retains ownership of 'packet', so ->packet_out() should not
- * modify or free it.
+ * The caller retains ownership of 'packet' and of 'ofpacts', so
+ * ->packet_out() should not modify or free them.
*
- * This function must validate that the 'n_actions' elements in 'actions'
- * are well-formed OpenFlow actions that can be correctly implemented by
- * the datapath. If not, then it should return an OpenFlow error code.
+ * This function must validate that it can implement 'ofpacts'. If not,
+ * then it should return an OpenFlow error code.
*
* 'flow' reflects the flow information for 'packet'. All of the
* information in 'flow' is extracted from 'packet', except for
* Returns 0 if successful, otherwise an OpenFlow error code. */
enum ofperr (*packet_out)(struct ofproto *ofproto, struct ofpbuf *packet,
const struct flow *flow,
- const union ofp_action *actions,
- size_t n_actions);
+ const struct ofpact *ofpacts,
+ size_t ofpacts_len);
/* ## ------------------------- ## */
/* ## OFPP_NORMAL configuration ## */
int ofproto_flow_mod(struct ofproto *, const struct ofputil_flow_mod *);
void ofproto_add_flow(struct ofproto *, const struct cls_rule *,
- const union ofp_action *, size_t n_actions);
+ const struct ofpact *ofpacts, size_t ofpacts_len);
bool ofproto_delete_flow(struct ofproto *, const struct cls_rule *);
void ofproto_flush_flows(struct ofproto *);
#include "meta-flow.h"
#include "netdev.h"
#include "nx-match.h"
+#include "ofp-actions.h"
#include "ofp-errors.h"
#include "ofp-print.h"
#include "ofp-util.h"
struct rule *rule; /* Rule being operated upon. */
enum ofoperation_type type; /* Type of operation. */
struct rule *victim; /* OFOPERATION_ADDING: Replaced rule. */
- union ofp_action *actions; /* OFOPERATION_MODIFYING: Replaced actions. */
- int n_actions; /* OFOPERATION_MODIFYING: # of old actions. */
+ struct ofpact *ofpacts; /* OFOPERATION_MODIFYING: Replaced actions. */
+ size_t ofpacts_len; /* OFOPERATION_MODIFYING: Bytes of ofpacts. */
ovs_be64 flow_cookie; /* Rule's old flow cookie. */
};
* (0...65535, inclusive) then the flow will be visible to OpenFlow
* controllers; otherwise, it will be hidden.
*
- * The caller retains ownership of 'cls_rule' and 'actions'.
+ * The caller retains ownership of 'cls_rule' and 'ofpacts'.
*
* This is a helper function for in-band control and fail-open. */
void
ofproto_add_flow(struct ofproto *ofproto, const struct cls_rule *cls_rule,
- const union ofp_action *actions, size_t n_actions)
+ const struct ofpact *ofpacts, size_t ofpacts_len)
{
const struct rule *rule;
rule = rule_from_cls_rule(classifier_find_rule_exactly(
&ofproto->tables[0].cls, cls_rule));
- if (!rule || !ofputil_actions_equal(rule->actions, rule->n_actions,
- actions, n_actions)) {
+ if (!rule || !ofpacts_equal(rule->ofpacts, rule->ofpacts_len,
+ ofpacts, ofpacts_len)) {
struct ofputil_flow_mod fm;
memset(&fm, 0, sizeof fm);
fm.cr = *cls_rule;
fm.buffer_id = UINT32_MAX;
- fm.actions = (union ofp_action *) actions;
- fm.n_actions = n_actions;
+ fm.ofpacts = xmemdup(ofpacts, ofpacts_len);
+ fm.ofpacts_len = ofpacts_len;
add_flow(ofproto, NULL, &fm, NULL);
+ free(fm.ofpacts);
}
}
ofproto_rule_destroy__(struct rule *rule)
{
if (rule) {
- free(rule->actions);
+ free(rule->ofpacts);
rule->ofproto->ofproto_class->rule_dealloc(rule);
}
}
}
/* Returns true if 'rule' has an OpenFlow OFPAT_OUTPUT or OFPAT_ENQUEUE action
- * that outputs to 'out_port' (output to OFPP_FLOOD and OFPP_ALL doesn't
- * count). */
+ * that outputs to 'port' (output to OFPP_FLOOD and OFPP_ALL doesn't count). */
static bool
-rule_has_out_port(const struct rule *rule, uint16_t out_port)
+rule_has_out_port(const struct rule *rule, uint16_t port)
{
- const union ofp_action *oa;
- size_t left;
-
- if (out_port == OFPP_NONE) {
- return true;
- }
- OFPUTIL_ACTION_FOR_EACH_UNSAFE (oa, left, rule->actions, rule->n_actions) {
- if (action_outputs_to_port(oa, htons(out_port))) {
- return true;
- }
- }
- return false;
+ return (port == OFPP_NONE
+ || ofpacts_output_to_port(rule->ofpacts, rule->ofpacts_len, port));
}
/* Executes the actions indicated by 'rule' on 'packet' and credits 'rule''s
struct ofproto *p = ofconn_get_ofproto(ofconn);
struct ofputil_packet_out po;
struct ofpbuf *payload;
+ uint64_t ofpacts_stub[1024 / 8];
+ struct ofpbuf ofpacts;
struct flow flow;
enum ofperr error;
error = reject_slave_controller(ofconn);
if (error) {
- return error;
+ goto exit;
}
/* Decode message. */
- error = ofputil_decode_packet_out(&po, opo);
+ ofpbuf_use_stub(&ofpacts, ofpacts_stub, sizeof ofpacts_stub);
+ error = ofputil_decode_packet_out(&po, opo, &ofpacts);
if (error) {
- return error;
+ goto exit_free_ofpacts;
}
/* Get payload. */
if (po.buffer_id != UINT32_MAX) {
error = ofconn_pktbuf_retrieve(ofconn, po.buffer_id, &payload, NULL);
if (error || !payload) {
- return error;
+ goto exit_free_ofpacts;
}
} else {
payload = xmalloc(sizeof *payload);
/* Send out packet. */
flow_extract(payload, 0, 0, po.in_port, &flow);
error = p->ofproto_class->packet_out(p, payload, &flow,
- po.actions, po.n_actions);
+ po.ofpacts, po.ofpacts_len);
ofpbuf_delete(payload);
+exit_free_ofpacts:
+ ofpbuf_uninit(&ofpacts);
+exit:
return error;
}
fs.hard_age = age_secs(now - rule->modified);
ofproto->ofproto_class->rule_get_stats(rule, &fs.packet_count,
&fs.byte_count);
- fs.actions = rule->actions;
- fs.n_actions = rule->n_actions;
+ fs.ofpacts = rule->ofpacts;
+ fs.ofpacts_len = rule->ofpacts_len;
ofputil_append_flow_stats_reply(&fs, &replies);
}
ofconn_send_replies(ofconn, &replies);
ds_put_format(results, "n_bytes=%"PRIu64", ", byte_count);
cls_rule_format(&rule->cr, results);
ds_put_char(results, ',');
- if (rule->n_actions > 0) {
- ofp_print_actions(results, rule->actions, rule->n_actions);
+ if (rule->ofpacts_len > 0) {
+ ofpacts_format(rule->ofpacts, rule->ofpacts_len, results);
} else {
ds_put_cstr(results, "drop");
}
* error code on failure, or OFPROTO_POSTPONE if the operation cannot be
* initiated now but may be retried later.
*
+ * Upon successful return, takes ownership of 'fm->ofpacts'. On failure,
+ * ownership remains with the caller.
+ *
* 'ofconn' is used to retrieve the packet buffer specified in ofm->buffer_id,
* if any. */
static enum ofperr
rule->hard_timeout = fm->hard_timeout;
rule->table_id = table - ofproto->tables;
rule->send_flow_removed = (fm->flags & OFPFF_SEND_FLOW_REM) != 0;
- rule->actions = ofputil_actions_clone(fm->actions, fm->n_actions);
- rule->n_actions = fm->n_actions;
+ rule->ofpacts = xmemdup(fm->ofpacts, fm->ofpacts_len);
+ rule->ofpacts_len = fm->ofpacts_len;
rule->evictable = true;
rule->eviction_group = NULL;
continue;
}
- if (!ofputil_actions_equal(fm->actions, fm->n_actions,
- rule->actions, rule->n_actions)) {
+ if (!ofpacts_equal(fm->ofpacts, fm->ofpacts_len,
+ rule->ofpacts, rule->ofpacts_len)) {
ofoperation_create(group, rule, OFOPERATION_MODIFY);
- rule->pending->actions = rule->actions;
- rule->pending->n_actions = rule->n_actions;
- rule->actions = ofputil_actions_clone(fm->actions, fm->n_actions);
- rule->n_actions = fm->n_actions;
- ofproto->ofproto_class->rule_modify_actions(rule);
+ rule->pending->ofpacts = rule->ofpacts;
+ rule->pending->ofpacts_len = rule->ofpacts_len;
+ rule->ofpacts = xmemdup(fm->ofpacts, fm->ofpacts_len);
+ rule->ofpacts_len = fm->ofpacts_len;
+ rule->ofproto->ofproto_class->rule_modify_actions(rule);
} else {
rule->modified = time_msec();
}
{
struct ofproto *ofproto = ofconn_get_ofproto(ofconn);
struct ofputil_flow_mod fm;
+ uint64_t ofpacts_stub[1024 / 8];
+ struct ofpbuf ofpacts;
enum ofperr error;
long long int now;
error = reject_slave_controller(ofconn);
if (error) {
- return error;
+ goto exit;
}
- error = ofputil_decode_flow_mod(&fm, oh, ofconn_get_protocol(ofconn));
+ ofpbuf_use_stub(&ofpacts, ofpacts_stub, sizeof ofpacts_stub);
+ error = ofputil_decode_flow_mod(&fm, oh, ofconn_get_protocol(ofconn),
+ &ofpacts);
if (error) {
- return error;
+ goto exit_free_ofpacts;
}
/* We do not support the OpenFlow 1.0 emergency flow cache, which is not
* required in OpenFlow 1.0.1 and removed from OpenFlow 1.1. */
if (fm.flags & OFPFF_EMERG) {
- /* There isn't a good fit for an error code, so just state that the
- * flow table is full. */
- return OFPERR_OFPFMFC_ALL_TABLES_FULL;
+ /* We do not support the emergency flow cache. It will hopefully get
+ * dropped from OpenFlow in the near future. There is no good error
+ * code, so just state that the flow table is full. */
+ error = OFPERR_OFPFMFC_ALL_TABLES_FULL;
+ } else {
+ error = handle_flow_mod__(ofconn_get_ofproto(ofconn), ofconn, &fm, oh);
}
-
- error = handle_flow_mod__(ofconn_get_ofproto(ofconn), ofconn, &fm, oh);
if (error) {
- return error;
+ goto exit_free_ofpacts;
}
/* Record the operation for logging a summary report. */
}
ofproto->last_op = now;
- return 0;
+exit_free_ofpacts:
+ ofpbuf_uninit(&ofpacts);
+exit:
+ return error;
}
static enum ofperr
hmap_remove(&group->ofproto->deletions, &op->hmap_node);
}
list_remove(&op->group_node);
- free(op->actions);
+ free(op->ofpacts);
free(op);
if (list_is_empty(&group->ops) && !list_is_empty(&group->ofproto_node)) {
if (!error) {
rule->modified = time_msec();
} else {
- free(rule->actions);
- rule->actions = op->actions;
- rule->n_actions = op->n_actions;
- op->actions = NULL;
+ free(rule->ofpacts);
+ rule->ofpacts = op->ofpacts;
+ rule->ofpacts_len = op->ofpacts_len;
+ op->ofpacts = NULL;
}
break;
tests/check-structs.at \
tests/daemon.at \
tests/daemon-py.at \
+ tests/ofp-actions.at \
tests/ofp-print.at \
tests/ofp-errors.at \
tests/ovs-ofctl.at \
[[usable protocols: any
chosen protocol: OpenFlow10-table_id
OFPT_FLOW_MOD (xid=0x1): ADD actions=learn(table=1)
-OFPT_FLOW_MOD (xid=0x2): ADD actions=learn(table=1,NXM_OF_VLAN_TCI[0..11],NXM_OF_ETH_DST[]=NXM_OF_ETH_SRC[],output:NXM_OF_IN_PORT[],load:0x000a->NXM_NX_REG0[5..10])
+OFPT_FLOW_MOD (xid=0x2): ADD actions=learn(table=1,NXM_OF_VLAN_TCI[0..11],NXM_OF_ETH_DST[]=NXM_OF_ETH_SRC[],output:NXM_OF_IN_PORT[],load:0xa->NXM_NX_REG0[5..10])
OFPT_FLOW_MOD (xid=0x3): ADD actions=learn(table=1,idle_timeout=10,hard_timeout=20,fin_idle_timeout=5,fin_hard_timeout=10,priority=10,cookie=0xfedcba9876543210,in_port=99,NXM_OF_ETH_DST[]=NXM_OF_ETH_SRC[],load:NXM_OF_IN_PORT[]->NXM_NX_REG1[16..31])
]])
AT_CLEANUP
AT_CHECK([ovs-ofctl parse-flows flows.txt], [0],
[[usable protocols: any
chosen protocol: OpenFlow10-table_id
-OFPT_FLOW_MOD (xid=0x1): ADD actions=learn(table=1,eth_type=0x800,load:0x00000005->NXM_OF_IP_DST[])
+OFPT_FLOW_MOD (xid=0x1): ADD actions=learn(table=1,eth_type=0x800,load:0x5->NXM_OF_IP_DST[])
OFPT_FLOW_MOD (xid=0x2): ADD ip actions=learn(table=1,load:NXM_OF_IP_DST[]->NXM_NX_REG1[])
OFPT_FLOW_MOD (xid=0x3): ADD ip actions=learn(table=1,eth_type=0x800,NXM_OF_IP_DST[])
]])
--- /dev/null
+AT_BANNER([OpenFlow actions])
+
+AT_SETUP([OpenFlow 1.0 action translation])
+AT_KEYWORDS([OF1.0])
+AT_DATA([test-data], [dnl
+# actions=LOCAL
+0000 0008 fffe 04d2
+
+# actions=CONTROLLER:1234
+0000 0008 fffd 04d2
+
+# actions=mod_vlan_vid:9
+0001 0008 0009 0000
+
+# actions=mod_vlan_pcp:6
+0002 0008 06 000000
+
+# actions=strip_vlan
+0003 0008 00000000
+
+# actions=mod_dl_src:00:11:22:33:44:55
+0004 0010 001122334455 000000000000
+
+# actions=mod_dl_dst:10:20:30:40:50:60
+0005 0010 102030405060 000000000000
+
+# actions=mod_nw_src:1.2.3.4
+0006 0008 01020304
+
+# actions=mod_nw_dst:192.168.0.1
+0007 0008 c0a80001
+
+# actions=mod_nw_tos:48
+0008 0008 30 000000
+
+# actions=mod_tp_src:80
+0009 0008 0050 0000
+
+# actions=mod_tp_dst:443
+000a 0008 01bb 0000
+
+# actions=enqueue:10q55
+000b 0010 000a 000000000000 00000037
+
+# actions=resubmit:5
+ffff 0010 00002320 0001 0005 00000000
+
+# actions=set_tunnel:0x12345678
+ffff 0010 00002320 0002 0000 12345678
+
+# actions=set_queue:2309737729
+ffff 0010 00002320 0004 0000 89abcd01
+
+# actions=pop_queue
+ffff 0010 00002320 0005 000000000000
+
+# actions=move:NXM_OF_IN_PORT[]->NXM_OF_VLAN_TCI[]
+ffff 0018 00002320 0006 0010 0000 0000 00000002 00000802
+
+# actions=load:0xf009->NXM_OF_VLAN_TCI[]
+ffff 0018 00002320 0007 000f 00000802 000000000000f009
+
+# actions=note:11.e9.9a.ad.67.f3
+ffff 0010 00002320 0008 11e99aad67f3
+
+# actions=set_tunnel64:0xc426384d49c53d60
+ffff 0018 00002320 0009 000000000000 c426384d49c53d60
+
+# actions=set_tunnel64:0x885f3298
+ffff 0018 00002320 0009 000000000000 00000000885f3298
+
+# actions=multipath(eth_src,50,modulo_n,1,0,NXM_NX_REG0[])
+ffff 0020 00002320 000a 0000 0032 0000 0000 0000 0000 0000 0000 001f 00010004
+
+# actions=autopath(2,NXM_NX_REG0[2..30])
+ffff 0018 00002320 000b 009c 00010004 00000002 00000000
+
+# actions=bundle(eth_src,0,hrw,ofport,slaves:4,8)
+ffff 0028 00002320 000c 0001 0000 0000 00000002 0002 0000 00000000 00000000 dnl
+0004 0008 00000000
+
+# actions=bundle_load(eth_src,0,hrw,ofport,NXM_NX_REG0[],slaves:4,8)
+ffff 0028 00002320 000d 0001 0000 0000 00000002 0002 001f 00010004 00000000 dnl
+0004 0008 00000000
+
+# actions=resubmit(10,5)
+ffff 0010 00002320 000e 000a 05 000000
+
+# actions=output:NXM_NX_REG1[5..10]
+ffff 0018 00002320 000f 0145 00010204 ffff 000000000000
+
+# actions=learn(table=2,idle_timeout=10,hard_timeout=20,fin_idle_timeout=2,fin_hard_timeout=4,priority=80,cookie=0x123456789abcdef0,NXM_OF_VLAN_TCI[0..11],NXM_OF_ETH_DST[]=NXM_OF_ETH_SRC[],output:NXM_OF_IN_PORT[])
+ffff 0048 00002320 0010 000a 0014 0050 123456789abcdef0 0000 02 00 0002 0004 dnl
+000c 00000802 0000 00000802 0000 dnl
+0030 00000406 0000 00000206 0000 dnl
+1010 00000002 0000 dnl
+00000000
+
+# actions=exit
+ffff 0010 00002320 0011 000000000000
+
+# actions=dec_ttl
+ffff 0010 00002320 0012 000000000000
+
+# actions=fin_timeout(idle_timeout=10,hard_timeout=20)
+ffff 0010 00002320 0013 000a 0014 0000
+
+# actions=controller(reason=invalid_ttl,max_len=1234,id=5678)
+ffff 0010 00002320 0014 04d2 162e 02 00
+
+])
+sed '/^[[#&]]/d' < test-data > input.txt
+sed -n 's/^# //p; /^$/p' < test-data > expout
+sed -n 's/^& //p' < test-data > experr
+AT_CAPTURE_FILE([input.txt])
+AT_CAPTURE_FILE([expout])
+AT_CAPTURE_FILE([experr])
+AT_CHECK(
+ [ovs-ofctl '-vPATTERN:console:%c|%p|%m' parse-ofp10-actions < input.txt],
+ [0], [expout], [experr])
+AT_CLEANUP
-/* Copyright (c) 2011 Nicira, Inc.
+/* Copyright (c) 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.
#include <stdlib.h>
#include "flow.h"
+#include "ofp-actions.h"
#include "ofpbuf.h"
#include "random.h"
return slave ? slave->enabled : false;
}
-static struct nx_action_bundle *
+static struct ofpact_bundle *
parse_bundle_actions(char *actions)
{
- struct nx_action_bundle *nab;
- struct ofpbuf b;
+ struct ofpact_bundle *bundle;
+ struct ofpbuf ofpacts;
+ struct ofpact *action;
- ofpbuf_init(&b, 0);
- bundle_parse_load(&b, actions);
- nab = ofpbuf_steal_data(&b);
- ofpbuf_uninit(&b);
+ ofpbuf_init(&ofpacts, 0);
+ bundle_parse_load(actions, &ofpacts);
+ action = ofpacts.data;
+ bundle = ofpact_get_BUNDLE(xmemdup(action, action->len));
+ ofpbuf_uninit(&ofpacts);
- if (ntohs(nab->n_slaves) > MAX_SLAVES) {
+ if (bundle->n_slaves > MAX_SLAVES) {
ovs_fatal(0, "At most %u slaves are supported", MAX_SLAVES);
}
- return nab;
+ return bundle;
}
static const char *
main(int argc, char *argv[])
{
bool ok = true;
- struct nx_action_bundle *nab;
+ struct ofpact_bundle *bundle;
struct flow *flows;
size_t i, n_permute, old_n_enabled;
struct slave_group sg;
ovs_fatal(0, "usage: %s bundle_action", program_name);
}
- nab = parse_bundle_actions(argv[1]);
+ bundle = parse_bundle_actions(argv[1]);
/* Generate 'slaves' array. */
sg.n_slaves = 0;
- for (i = 0; i < ntohs(nab->n_slaves); i++) {
- uint16_t slave_id = bundle_get_slave(nab, i);
+ for (i = 0; i < bundle->n_slaves; i++) {
+ uint16_t slave_id = bundle->slaves[i];
if (slave_lookup(&sg, slave_id)) {
ovs_fatal(0, "Redundant slaves are not supported. ");
flows[i].regs[0] = OFPP_NONE;
}
- if (bundle_check(nab, 1024, flows)) {
- ovs_fatal(0, "Bundle action fails to check.");
- }
-
/* Cycles through each possible liveness permutation for the given
* n_slaves. The initial state is equivalent to all slaves down, so we
* skip it by starting at i = 1. We do one extra iteration to cover
uint16_t old_slave_id, ofp_port;
old_slave_id = flow->regs[0];
- ofp_port = bundle_execute(nab, flow, slave_enabled_cb, &sg);
- bundle_execute_load(nab, flow, slave_enabled_cb, &sg);
- if (flow->regs[0] != ofp_port) {
- ovs_fatal(0, "bundle_execute_load() and bundle_execute() "
- "disagree");
- }
+ ofp_port = bundle_execute(bundle, flow, slave_enabled_cb, &sg);
+ flow->regs[0] = ofp_port;
- if (flow->regs[0] != OFPP_NONE) {
- slave_lookup(&sg, flow->regs[0])->flow_count++;
+ if (ofp_port != OFPP_NONE) {
+ slave_lookup(&sg, ofp_port)->flow_count++;
}
- if (old_slave_id != flow->regs[0]) {
+ if (old_slave_id != ofp_port) {
changed++;
}
}
- if (nab->algorithm == htons(NX_BD_ALG_ACTIVE_BACKUP)) {
+ if (bundle->algorithm == NX_BD_ALG_ACTIVE_BACKUP) {
perfect = active == old_active ? 0.0 : 1.0;
} else {
if (old_n_enabled || n_enabled) {
if (slave->enabled) {
double perfect_fp;
- if (nab->algorithm == htons(NX_BD_ALG_ACTIVE_BACKUP)) {
+ if (bundle->algorithm == NX_BD_ALG_ACTIVE_BACKUP) {
perfect_fp = j == active ? 1.0 : 0.0;
} else {
perfect_fp = 1.0 / n_enabled;
old_n_enabled = n_enabled;
}
- free(nab);
+ free(bundle);
free(flows);
return ok ? 0 : 1;
}
/*
- * Copyright (c) 2010 Nicira, Inc.
+ * Copyright (c) 2010, 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.
#include <stdlib.h>
#include "flow.h"
+#include "ofp-actions.h"
#include "random.h"
#include "util.h"
main(int argc, char *argv[])
{
enum { MP_MAX_LINKS = 63 };
- struct nx_action_multipath mp;
+ struct ofpact_multipath mp;
bool ok = true;
int n;
random_bytes(&flow, sizeof flow);
- mp.max_link = htons(n - 1);
+ mp.max_link = n - 1;
multipath_execute(&mp, &flow);
old_link = flow.regs[0];
- mp.max_link = htons(n);
+ mp.max_link = n;
multipath_execute(&mp, &flow);
new_link = flow.regs[0];
"stddev/expected=%.4f\n",
n, n + 1, disruption, perfect, distribution);
- switch (ntohs(mp.algorithm)) {
+ switch (mp.algorithm) {
case NX_MP_ALG_MODULO_N:
if (disruption < (n < 2 ? .25 : .5)) {
fprintf(stderr, "%d -> %d: disruption=%.2f < .5\n",
m4_include([tests/check-structs.at])
m4_include([tests/daemon.at])
m4_include([tests/daemon-py.at])
+m4_include([tests/ofp-actions.at])
m4_include([tests/ofp-print.at])
m4_include([tests/ofp-errors.at])
m4_include([tests/ovs-ofctl.at])
#include "compiler.h"
#include "dirs.h"
#include "dynamic-string.h"
-#include "netlink.h"
#include "nx-match.h"
#include "odp-util.h"
+#include "ofp-actions.h"
#include "ofp-errors.h"
#include "ofp-parse.h"
#include "ofp-print.h"
struct ofputil_flow_mod *fm = &fms[i];
transact_noreply(vconn, ofputil_encode_flow_mod(fm, protocol));
- free(fm->actions);
+ free(fm->ofpacts);
}
vconn_close(vconn);
}
do_packet_out(int argc, char *argv[])
{
struct ofputil_packet_out po;
- struct ofpbuf actions;
+ struct ofpbuf ofpacts;
struct vconn *vconn;
int i;
- ofpbuf_init(&actions, sizeof(union ofp_action));
- parse_ofp_actions(argv[3], &actions);
+ ofpbuf_init(&ofpacts, 64);
+ parse_ofpacts(argv[3], &ofpacts);
po.buffer_id = UINT32_MAX;
po.in_port = (!strcasecmp(argv[2], "none") ? OFPP_NONE
: !strcasecmp(argv[2], "local") ? OFPP_LOCAL
: str_to_port_no(argv[1], argv[2]));
- po.actions = actions.data;
- po.n_actions = actions.size / sizeof(union ofp_action);
+ po.ofpacts = ofpacts.data;
+ po.ofpacts_len = ofpacts.size;
open_vconn(argv[1], &vconn);
for (i = 4; i < argc; i++) {
ofpbuf_delete(packet);
}
vconn_close(vconn);
- ofpbuf_uninit(&actions);
+ ofpbuf_uninit(&ofpacts);
}
static void
uint16_t idle_timeout;
uint16_t hard_timeout;
uint16_t flags;
- union ofp_action *actions;
- size_t n_actions;
+ struct ofpact *ofpacts;
+ size_t ofpacts_len;
};
/* Frees 'version' and the data that it owns. */
fte_version_free(struct fte_version *version)
{
if (version) {
- free(version->actions);
+ free(version->ofpacts);
free(version);
}
}
return (a->cookie == b->cookie
&& a->idle_timeout == b->idle_timeout
&& a->hard_timeout == b->hard_timeout
- && a->n_actions == b->n_actions
- && !memcmp(a->actions, b->actions,
- a->n_actions * sizeof *a->actions));
+ && ofpacts_equal(a->ofpacts, a->ofpacts_len,
+ b->ofpacts, b->ofpacts_len));
}
/* Prints 'version' on stdout. Expects the caller to have printed the rule
}
ds_init(&s);
- ofp_print_actions(&s, version->actions, version->n_actions);
+ ofpacts_format(version->ofpacts, version->ofpacts_len, &s);
printf(" %s\n", ds_cstr(&s));
ds_destroy(&s);
}
version->idle_timeout = fm.idle_timeout;
version->hard_timeout = fm.hard_timeout;
version->flags = fm.flags & (OFPFF_SEND_FLOW_REM | OFPFF_EMERG);
- version->actions = fm.actions;
- version->n_actions = fm.n_actions;
+ version->ofpacts = fm.ofpacts;
+ version->ofpacts_len = fm.ofpacts_len;
usable_protocols &= ofputil_usable_protocols(&fm.cr);
for (;;) {
struct fte_version *version;
struct ofputil_flow_stats fs;
+ struct ofpbuf ofpacts;
int retval;
- retval = ofputil_decode_flow_stats_reply(&fs, reply, false);
+ ofpbuf_init(&ofpacts, 64);
+ retval = ofputil_decode_flow_stats_reply(&fs, reply, false,
+ &ofpacts);
if (retval) {
+ ofpbuf_uninit(&ofpacts);
if (retval != EOF) {
ovs_fatal(0, "parse error in reply");
}
version->idle_timeout = fs.idle_timeout;
version->hard_timeout = fs.hard_timeout;
version->flags = 0;
- version->n_actions = fs.n_actions;
- version->actions = xmemdup(fs.actions,
- fs.n_actions * sizeof *fs.actions);
+ version->ofpacts = ofpbuf_steal_data(&ofpacts);
+ version->ofpacts_len = ofpacts.size;
fte_insert(cls, &fs.rule, version, index);
}
fm.flags = version->flags;
if (command == OFPFC_ADD || command == OFPFC_MODIFY ||
command == OFPFC_MODIFY_STRICT) {
- fm.actions = version->actions;
- fm.n_actions = version->n_actions;
+ fm.ofpacts = version->ofpacts;
+ fm.ofpacts_len = version->ofpacts_len;
} else {
- fm.actions = NULL;
- fm.n_actions = 0;
+ fm.ofpacts = NULL;
+ fm.ofpacts_len = 0;
}
ofm = ofputil_encode_flow_mod(&fm, protocol);
ofp_print(stdout, msg->data, msg->size, verbosity);
ofpbuf_delete(msg);
- free(fm->actions);
+ free(fm->ofpacts);
}
}
return do_parse_nxm__(true);
}
+static void
+print_differences(const void *a_, size_t a_len,
+ const void *b_, size_t b_len)
+{
+ const uint8_t *a = a_;
+ const uint8_t *b = b_;
+ size_t i;
+
+ for (i = 0; i < MIN(a_len, b_len); i++) {
+ if (a[i] != b[i]) {
+ printf("%2zu: %02"PRIx8" -> %02"PRIx8"\n", i, a[i], b[i]);
+ }
+ }
+ for (i = a_len; i < b_len; i++) {
+ printf("%2zu: (none) -> %02"PRIx8"\n", i, b[i]);
+ }
+ for (i = b_len; i < a_len; i++) {
+ printf("%2zu: %02"PRIx8" -> (none)\n", i, a[i]);
+ }
+}
+
+/* "parse-ofp10-actions": reads a series of OpenFlow 1.0 action specifications
+ * as hex bytes from stdin, converts them to ofpacts, prints them as strings
+ * on stdout, and then converts them back to hex bytes and prints any
+ * differences from the input. */
+static void
+do_parse_ofp10_actions(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
+{
+ struct ds in;
+
+ ds_init(&in);
+ while (!ds_get_preprocessed_line(&in, stdin)) {
+ struct ofpbuf of10_out;
+ struct ofpbuf of10_in;
+ struct ofpbuf ofpacts;
+ enum ofperr error;
+ size_t size;
+ struct ds s;
+
+ /* Parse hex bytes. */
+ ofpbuf_init(&of10_in, 0);
+ if (ofpbuf_put_hex(&of10_in, ds_cstr(&in), NULL)[0] != '\0') {
+ ovs_fatal(0, "Trailing garbage in hex data");
+ }
+
+ /* Convert to ofpacts. */
+ ofpbuf_init(&ofpacts, 0);
+ size = of10_in.size;
+ error = ofpacts_pull_openflow(&of10_in, of10_in.size, &ofpacts);
+ if (error) {
+ printf("bad OF1.1 actions: %s\n\n", ofperr_get_name(error));
+ ofpbuf_uninit(&ofpacts);
+ ofpbuf_uninit(&of10_in);
+ continue;
+ }
+ ofpbuf_push_uninit(&of10_in, size);
+
+ /* Print cls_rule. */
+ ds_init(&s);
+ ofpacts_format(ofpacts.data, ofpacts.size, &s);
+ puts(ds_cstr(&s));
+ ds_destroy(&s);
+
+ /* Convert back to ofp10 actions and print differences from input. */
+ ofpbuf_init(&of10_out, 0);
+ ofpacts_to_openflow(ofpacts.data, ofpacts.size, &of10_out);
+
+ print_differences(of10_in.data, of10_in.size,
+ of10_out.data, of10_out.size);
+ putchar('\n');
+
+ ofpbuf_uninit(&ofpacts);
+ ofpbuf_uninit(&of10_in);
+ ofpbuf_uninit(&of10_out);
+ }
+ ds_destroy(&in);
+}
+
/* "parse-ofp11-match": reads a series of ofp11_match specifications as hex
* bytes from stdin, converts them to cls_rules, prints them as strings on
* stdout, and then converts them back to hex bytes and prints any differences
{ "parse-nx-match", 0, 0, do_parse_nxm },
{ "parse-nxm", 0, 0, do_parse_nxm },
{ "parse-oxm", 0, 0, do_parse_oxm },
+ { "parse-ofp10-actions", 0, 0, do_parse_ofp10_actions },
{ "parse-ofp11-match", 0, 0, do_parse_ofp11_match },
{ "print-error", 1, 1, do_print_error },
{ "ofp-print", 1, 2, do_ofp_print },