aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--DESIGN20
-rw-r--r--NEWS3
-rw-r--r--lib/automake.mk2
-rw-r--r--lib/autopath.c70
-rw-r--r--lib/autopath.h15
-rw-r--r--lib/bundle.c277
-rw-r--r--lib/bundle.h26
-rw-r--r--lib/compiler.h20
-rw-r--r--lib/learn.c672
-rw-r--r--lib/learn.h17
-rw-r--r--lib/learning-switch.c99
-rw-r--r--lib/multipath.c159
-rw-r--r--lib/multipath.h18
-rw-r--r--lib/nx-match.c217
-rw-r--r--lib/nx-match.h35
-rw-r--r--lib/ofp-actions.c1218
-rw-r--r--lib/ofp-actions.h485
-rw-r--r--lib/ofp-parse.c252
-rw-r--r--lib/ofp-parse.h2
-rw-r--r--lib/ofp-print.c291
-rw-r--r--lib/ofp-print.h2
-rw-r--r--lib/ofp-util.c526
-rw-r--r--lib/ofp-util.h69
-rw-r--r--ofproto/connmgr.c15
-rw-r--r--ofproto/fail-open.c14
-rw-r--r--ofproto/in-band.c37
-rw-r--r--ofproto/ofproto-dpif.c401
-rw-r--r--ofproto/ofproto-provider.h34
-rw-r--r--ofproto/ofproto.c122
-rw-r--r--tests/automake.mk1
-rw-r--r--tests/learn.at4
-rw-r--r--tests/ofp-actions.at121
-rw-r--r--tests/test-bundle.c55
-rw-r--r--tests/test-multipath.c11
-rw-r--r--tests/testsuite.at1
-rw-r--r--utilities/ovs-ofctl.c133
36 files changed, 3356 insertions, 2088 deletions
diff --git a/DESIGN b/DESIGN
index a3a62b2e..7dd6efa2 100644
--- a/DESIGN
+++ b/DESIGN
@@ -612,6 +612,26 @@ The following are explicitly *not* supported by in-band control:
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
===========
diff --git a/NEWS b/NEWS
index 1966ac21..84f1ec11 100644
--- a/NEWS
+++ b/NEWS
@@ -1,6 +1,9 @@
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.
diff --git a/lib/automake.mk b/lib/automake.mk
index feac1d45..de4ec031 100644
--- a/lib/automake.mk
+++ b/lib/automake.mk
@@ -96,6 +96,8 @@ lib_libopenvswitch_a_SOURCES = \
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 \
diff --git a/lib/autopath.c b/lib/autopath.c
index 9511a6d2..93fedd1a 100644
--- a/lib/autopath.c
+++ b/lib/autopath.c
@@ -1,5 +1,5 @@
/*
- * 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.
@@ -24,6 +24,7 @@
#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"
@@ -31,32 +32,21 @@
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_);
}
@@ -65,33 +55,51 @@ autopath_parse(struct nx_action_autopath *ap, const char *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);
}
diff --git a/lib/autopath.h b/lib/autopath.h
index 480d40ae..337e7d1f 100644
--- a/lib/autopath.h
+++ b/lib/autopath.h
@@ -1,5 +1,5 @@
/*
- * 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.
@@ -22,15 +22,20 @@
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 */
diff --git a/lib/bundle.c b/lib/bundle.c
index a2059740..c6b1f75e 100644
--- a/lib/bundle.c
+++ b/lib/bundle.c
@@ -25,6 +25,7 @@
#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"
@@ -35,14 +36,13 @@
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;
}
@@ -52,18 +52,18 @@ execute_ab(const struct nx_action_bundle *nab,
}
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) {
@@ -73,33 +73,26 @@ execute_hrw(const struct nx_action_bundle *nab, const struct flow *flow,
}
}
- 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
@@ -107,41 +100,43 @@ bundle_execute_load(const struct nx_action_bundle *nab, struct flow *flow,
* 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)) {
@@ -150,34 +145,61 @@ bundle_check(const struct nx_action_bundle *nab, int max_ports,
}
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
@@ -185,23 +207,50 @@ bundle_check(const struct nx_action_bundle *nab, int max_ports,
* 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);
@@ -212,72 +261,56 @@ bundle_parse__(struct ofpbuf *b, const char *s, char **save_ptr,
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;
@@ -290,15 +323,15 @@ bundle_parse(struct ofpbuf *b, const char *s)
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;
@@ -312,22 +345,22 @@ bundle_parse_load(struct ofpbuf *b, const char *s)
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;
@@ -338,43 +371,23 @@ bundle_format(const struct nx_action_bundle *nab, struct ds *s)
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, ")");
diff --git a/lib/bundle.h b/lib/bundle.h
index 4c0dff5d..5b6bb674 100644
--- a/lib/bundle.h
+++ b/lib/bundle.h
@@ -1,4 +1,4 @@
-/* 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.
@@ -27,29 +27,23 @@
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 */
diff --git a/lib/compiler.h b/lib/compiler.h
index 75e86103..27612a74 100644
--- a/lib/compiler.h
+++ b/lib/compiler.h
@@ -1,5 +1,5 @@
/*
- * 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.
@@ -37,4 +37,22 @@
#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 */
diff --git a/lib/learn.c b/lib/learn.c
index 5478b748..28b8012c 100644
--- a/lib/learn.c
+++ b/lib/learn.c
@@ -22,6 +22,7 @@
#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"
@@ -46,19 +47,6 @@ get_be32(const void **pp)
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)
{
@@ -90,105 +78,85 @@ learn_min_len(uint16_t header)
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;
}
@@ -196,98 +164,50 @@ learn_check(const struct nx_action_learn *learn, const struct flow *flow)
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
@@ -314,19 +234,150 @@ put_u32(struct ofpbuf *b, uint32_t x)
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, "->");
@@ -377,9 +428,8 @@ learn_parse_load_immediate(const char *s, struct learn_spec *spec)
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;
@@ -428,17 +478,15 @@ learn_parse_spec(const char *orig, char *name, char *value,
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') {
@@ -455,8 +503,8 @@ learn_parse_spec(const char *orig, char *name, char *value,
}
/* 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.
*
@@ -466,29 +514,23 @@ learn_parse_spec(const char *orig, char *name, char *value,
*
* 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) {
@@ -496,73 +538,50 @@ learn_parse(struct ofpbuf *b, char *arg, const struct flow *flow)
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) {
@@ -574,169 +593,106 @@ learn_parse(struct ofpbuf *b, char *arg, const struct flow *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, ')');
}
diff --git a/lib/learn.h b/lib/learn.h
index 28591727..adf597e9 100644
--- a/lib/learn.h
+++ b/lib/learn.h
@@ -1,5 +1,5 @@
/*
- * 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.
@@ -22,6 +22,7 @@
struct ds;
struct flow;
struct ofpbuf;
+struct ofpact_learn;
struct ofputil_flow_mod;
struct nx_action_learn;
@@ -30,11 +31,15 @@ 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 */
diff --git a/lib/learning-switch.c b/lib/learning-switch.c
index 6b74f82f..cb0e49bc 100644
--- a/lib/learning-switch.c
+++ b/lib/learning-switch.c
@@ -29,6 +29,7 @@
#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"
@@ -56,6 +57,7 @@ struct lswitch {
* 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. */
@@ -81,7 +83,7 @@ static void send_features_request(struct lswitch *, struct rconn *);
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 *);
@@ -92,6 +94,7 @@ static void process_echo_request(struct lswitch *, struct rconn *,
struct lswitch *
lswitch_create(struct rconn *rconn, const struct lswitch_config *cfg)
{
+ enum ofputil_protocol protocol;
struct lswitch *sw;
sw = xzalloc(sizeof *sw);
@@ -139,18 +142,13 @@ lswitch_create(struct rconn *rconn, const struct lswitch_config *cfg)
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.
@@ -181,6 +179,7 @@ lswitch_create(struct rconn *rconn, const struct lswitch_config *cfg)
rconn_get_name(rconn), strerror(error));
}
}
+ sw->protocol = protocol;
return sw;
}
@@ -250,6 +249,7 @@ lswitch_process_packet(struct lswitch *sw, struct rconn *rconn,
break;
case OFPUTIL_OFPT_PACKET_IN:
+ case OFPUTIL_NXT_PACKET_IN:
process_packet_in(sw, rconn, msg->data);
break;
@@ -292,7 +292,6 @@ lswitch_process_packet(struct lswitch *sw, struct rconn *rconn,
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:
@@ -439,67 +438,58 @@ get_queue_id(const struct lswitch *sw, uint16_t in_port)
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;
@@ -507,31 +497,38 @@ process_packet_in(struct lswitch *sw, struct rconn *rconn,
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));
}
}
diff --git a/lib/multipath.c b/lib/multipath.c
index 8d932111..f6a1a0ae 100644
--- a/lib/multipath.c
+++ b/lib/multipath.c
@@ -22,8 +22,8 @@
#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"
@@ -34,37 +34,66 @@ VLOG_DEFINE_THIS_MODULE(multipath);
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
-/* 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);
}
/* multipath_execute(). */
@@ -72,19 +101,17 @@ multipath_check(const struct nx_action_multipath *mp, const struct flow *flow)
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
@@ -162,15 +189,17 @@ multipath_algorithm(uint32_t hash, enum nx_mp_algorithm algorithm,
NOT_REACHED();
}
-/* 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);
@@ -178,28 +207,28 @@ multipath_parse(struct nx_action_multipath *mp, const char *s_)
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);
}
@@ -208,34 +237,29 @@ multipath_parse(struct nx_action_multipath *mp, const char *s_)
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;
@@ -253,9 +277,8 @@ multipath_format(const struct nx_action_multipath *mp, struct ds *s)
}
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, ')');
}
diff --git a/lib/multipath.h b/lib/multipath.h
index 2cd646cb..1b5160dd 100644
--- a/lib/multipath.h
+++ b/lib/multipath.h
@@ -1,5 +1,5 @@
/*
- * 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.
@@ -23,18 +23,24 @@
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 */
diff --git a/lib/nx-match.c b/lib/nx-match.c
index 92e94f88..3e3b4c69 100644
--- a/lib/nx-match.c
+++ b/lib/nx-match.c
@@ -23,6 +23,7 @@
#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"
@@ -789,199 +790,183 @@ nx_match_from_string(const char *s, struct ofpbuf *b)
}
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);
}
/* 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);
+}
+
+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);
}
-
-/* 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);
}
-
+
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);
}
-/* 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);
}
+
+/* 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);
}
diff --git a/lib/nx-match.h b/lib/nx-match.h
index c814275f..6248b2a4 100644
--- a/lib/nx-match.h
+++ b/lib/nx-match.h
@@ -21,6 +21,7 @@
#include <sys/types.h>
#include <netinet/in.h>
#include "flow.h"
+#include "ofp-errors.h"
#include "openvswitch/types.h"
#include "ofp-errors.h"
@@ -28,6 +29,8 @@ struct cls_rule;
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;
@@ -49,19 +52,31 @@ int nx_put_match(struct ofpbuf *, bool oxm, const struct cls_rule *,
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);
@@ -85,10 +100,6 @@ nxm_decode_n_bits(ovs_be16 ofs_nbits)
{
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);
BUILD_ASSERT_DECL(FLOW_WC_SEQ == 12);
/* Upper bound on the length of an nx_match. The longest nx_match (an
diff --git a/lib/ofp-actions.c b/lib/ofp-actions.c
new file mode 100644
index 00000000..12cc0b0b
--- /dev/null
+++ b/lib/ofp-actions.c
@@ -0,0 +1,1218 @@
+/*
+ * 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);
+
+/* 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;
+}
+
+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;
+}
+
+/* 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();
+ }
+}
+
+/* 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);
+ }
+}
+
+/* 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);
+}
+
+/* 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);
+ }
+ }
+}
+
+/* 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;
+}
+
+/* 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);
+ }
+}
diff --git a/lib/ofp-actions.h b/lib/ofp-actions.h
new file mode 100644
index 00000000..59b9846b
--- /dev/null
+++ b/lib/ofp-actions.h
@@ -0,0 +1,485 @@
+/*
+ * 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))
+
+/* 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 */
diff --git a/lib/ofp-parse.c b/lib/ofp-parse.c
index 1d331bb4..6236e500 100644
--- a/lib/ofp-parse.c
+++ b/lib/ofp-parse.c
@@ -28,9 +28,10 @@
#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"
@@ -122,107 +123,73 @@ str_to_ip(const char *str, ovs_be32 *ip)
*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;
@@ -238,32 +205,27 @@ parse_note(struct ofpbuf *b, const char *arg)
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, &note->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);
}
@@ -301,121 +263,142 @@ parse_controller(struct ofpbuf *b, char *arg)
}
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:
@@ -423,29 +406,29 @@ parse_named_action(enum ofputil_action_code code, const struct flow *flow,
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;
@@ -458,10 +441,8 @@ str_to_action(const struct flow *flow, char *str, struct ofpbuf *b)
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");
@@ -471,12 +452,13 @@ str_to_action(const struct flow *flow, char *str, struct ofpbuf *b)
}
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 {
@@ -694,15 +676,15 @@ parse_ofp_str(struct ofputil_flow_mod *fm, int command, const char *str_,
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);
@@ -714,10 +696,10 @@ parse_ofp_str(struct ofputil_flow_mod *fm, int command, const char *str_,
* 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);
}
diff --git a/lib/ofp-parse.h b/lib/ofp-parse.h
index 3e5e62a6..e9303885 100644
--- a/lib/ofp-parse.h
+++ b/lib/ofp-parse.h
@@ -40,7 +40,7 @@ void parse_ofp_flow_stats_request_str(struct ofputil_flow_stats_request *,
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 *);
diff --git a/lib/ofp-print.c b/lib/ofp-print.c
index 335ebed9..c7c8a190 100644
--- a/lib/ofp-print.c
+++ b/lib/ofp-print.c
@@ -36,6 +36,7 @@
#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"
@@ -154,278 +155,17 @@ ofp_print_packet_in(struct ds *string, const struct ofp_header *oh,
}
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;
}
@@ -434,7 +174,7 @@ ofp_print_packet_out(struct ds *string, const struct ofp_packet_out *opo,
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);
@@ -448,6 +188,8 @@ ofp_print_packet_out(struct ds *string, const struct ofp_packet_out *opo,
ds_put_format(string, " buffer=0x%08"PRIx32, po.buffer_id);
}
ds_put_char(string, '\n');
+
+ ofpbuf_uninit(&ofpacts);
}
/* qsort comparison function. */
@@ -928,11 +670,14 @@ ofp_print_flow_mod(struct ds *s, const struct ofp_header *oh,
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;
}
@@ -1027,7 +772,8 @@ ofp_print_flow_mod(struct ds *s, const struct ofp_header *oh,
}
}
- ofp_print_actions(s, fm.actions, fm.n_actions);
+ ofpacts_format(fm.ofpacts, fm.ofpacts_len, s);
+ ofpbuf_uninit(&ofpacts);
}
static void
@@ -1229,14 +975,16 @@ ofp_print_flow_stats_request(struct ds *string,
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***");
@@ -1269,8 +1017,9 @@ ofp_print_flow_stats_reply(struct ds *string, const struct ofp_header *oh)
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
diff --git a/lib/ofp-print.h b/lib/ofp-print.h
index ae868a4b..49bcfcde 100644
--- a/lib/ofp-print.h
+++ b/lib/ofp-print.h
@@ -25,7 +25,6 @@
struct ofp_flow_mod;
struct ofp10_match;
struct ds;
-union ofp_action;
#ifdef __cplusplus
extern "C" {
@@ -34,7 +33,6 @@ 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);
diff --git a/lib/ofp-util.c b/lib/ofp-util.c
index 3329ed7f..4864815c 100644
--- a/lib/ofp-util.c
+++ b/lib/ofp-util.c
@@ -32,6 +32,7 @@
#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"
@@ -1644,11 +1645,17 @@ ofputil_make_flow_mod_table_id(bool flow_mod_table_id)
* 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;
@@ -1663,12 +1670,8 @@ ofputil_decode_flow_mod(struct ofputil_flow_mod *fm,
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
@@ -1683,6 +1686,12 @@ ofputil_decode_flow_mod(struct ofputil_flow_mod *fm,
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);
@@ -1705,7 +1714,7 @@ ofputil_decode_flow_mod(struct ofputil_flow_mod *fm,
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;
}
@@ -1727,6 +1736,8 @@ ofputil_decode_flow_mod(struct ofputil_flow_mod *fm,
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;
@@ -1744,7 +1755,6 @@ struct ofpbuf *
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;
@@ -1758,7 +1768,7 @@ ofputil_encode_flow_mod(const struct ofputil_flow_mod *fm,
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;
@@ -1773,7 +1783,7 @@ ofputil_encode_flow_mod(const struct ofputil_flow_mod *fm,
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);
@@ -1794,7 +1804,9 @@ ofputil_encode_flow_mod(const struct ofputil_flow_mod *fm,
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;
}
@@ -1988,12 +2000,17 @@ ofputil_flow_stats_request_usable_protocols(
* '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;
@@ -2031,8 +2048,7 @@ ofputil_decode_flow_stats_reply(struct ofputil_flow_stats *fs,
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;
}
@@ -2050,7 +2066,7 @@ ofputil_decode_flow_stats_reply(struct ofputil_flow_stats *fs,
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) {
@@ -2071,9 +2087,8 @@ ofputil_decode_flow_stats_reply(struct ofputil_flow_stats *fs,
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;
}
@@ -2099,6 +2114,9 @@ ofputil_decode_flow_stats_reply(struct ofputil_flow_stats *fs,
NOT_REACHED();
}
+ fs->ofpacts = ofpacts->data;
+ fs->ofpacts_len = ofpacts->size;
+
return 0;
}
@@ -2119,16 +2137,14 @@ void
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);
@@ -2143,17 +2159,14 @@ ofputil_append_flow_stats_reply(const struct ofputil_flow_stats *fs,
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);
@@ -2167,15 +2180,19 @@ ofputil_append_flow_stats_reply(const struct ofputil_flow_stats *fs,
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
@@ -2501,9 +2518,18 @@ ofputil_packet_in_reason_from_string(const char *s,
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;
@@ -2520,11 +2546,12 @@ ofputil_decode_packet_out(struct ofputil_packet_out *po,
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;
@@ -3070,25 +3097,27 @@ struct ofpbuf *
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;
@@ -3362,6 +3391,20 @@ ofputil_append_stats_reply(size_t len, struct list *replies)
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)
@@ -3394,58 +3437,6 @@ ofputil_nxstats_body_len(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)
@@ -3661,280 +3652,6 @@ size_t ofputil_count_phy_ports(uint8_t ofp_version, struct ofpbuf *b)
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.
@@ -4017,22 +3734,6 @@ ofputil_put_action(enum ofputil_action_code code, struct ofpbuf *buf)
}
#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
@@ -4140,57 +3841,6 @@ ofputil_normalize_rule(struct cls_rule *rule)
}
}
-/* 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
diff --git a/lib/ofp-util.h b/lib/ofp-util.h
index 3f9e440b..30e04c4b 100644
--- a/lib/ofp-util.h
+++ b/lib/ofp-util.h
@@ -22,6 +22,7 @@
#include <stddef.h>
#include <stdint.h>
#include "classifier.h"
+#include "compiler.h"
#include "flow.h"
#include "netdev.h"
#include "openflow/nicira-ext.h"
@@ -236,13 +237,14 @@ struct ofputil_flow_mod {
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);
@@ -279,13 +281,14 @@ struct ofputil_flow_stats {
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);
@@ -352,12 +355,13 @@ struct ofputil_packet_out {
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 {
@@ -531,6 +535,7 @@ void ofputil_start_stats_reply(const struct ofp_stats_msg *request,
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,
@@ -542,13 +547,7 @@ size_t ofputil_stats_body_len(const struct ofp_header *);
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);
@@ -597,7 +596,7 @@ bool ofputil_frag_handling_from_string(const char *, enum ofp_config_flags *);
*
* (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,
@@ -612,10 +611,6 @@ 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);
@@ -644,38 +639,6 @@ 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);
diff --git a/ofproto/connmgr.c b/ofproto/connmgr.c
index 8cdaa1f8..87dc2ad3 100644
--- a/ofproto/connmgr.c
+++ b/ofproto/connmgr.c
@@ -25,6 +25,7 @@
#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"
@@ -1573,15 +1574,17 @@ connmgr_flushed(struct connmgr *mgr)
* 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);
}
}
diff --git a/ofproto/fail-open.c b/ofproto/fail-open.c
index 912dc4e4..495197e7 100644
--- a/ofproto/fail-open.c
+++ b/ofproto/fail-open.c
@@ -23,6 +23,7 @@
#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"
@@ -214,18 +215,19 @@ fail_open_flushed(struct fail_open *fo)
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);
}
}
diff --git a/ofproto/in-band.c b/ofproto/in-band.c
index 2013869c..43461ad4 100644
--- a/ofproto/in-band.c
+++ b/ofproto/in-band.c
@@ -1,5 +1,5 @@
/*
- * 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.
@@ -29,6 +29,7 @@
#include "netdev.h"
#include "netlink.h"
#include "odp-util.h"
+#include "ofp-actions.h"
#include "ofproto.h"
#include "ofpbuf.h"
#include "ofproto-provider.h"
@@ -402,32 +403,17 @@ update_rules(struct in_band *ib)
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);
@@ -437,7 +423,8 @@ in_band_run(struct in_band *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:
@@ -451,6 +438,8 @@ in_band_run(struct in_band *ib)
}
}
+ ofpbuf_uninit(&ofpacts);
+
return ib->n_remotes || !hmap_is_empty(&ib->rules);
}
diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c
index f51182af..2451d44d 100644
--- a/ofproto/ofproto-dpif.c
+++ b/ofproto/ofproto-dpif.c
@@ -42,6 +42,7 @@
#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"
@@ -281,11 +282,11 @@ static void action_xlate_ctx_init(struct action_xlate_ctx *,
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,
@@ -804,7 +805,7 @@ construct(struct ofproto *ofproto_)
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;
@@ -821,8 +822,8 @@ add_internal_flow(struct ofproto_dpif *ofproto, int id,
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) {
@@ -840,26 +841,28 @@ add_internal_flow(struct ofproto_dpif *ofproto, int id,
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;
}
@@ -2864,7 +2867,7 @@ handle_flow_miss_without_facet(struct flow_miss *miss,
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) {
@@ -3699,8 +3702,8 @@ facet_learn(struct facet *facet)
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
@@ -3761,10 +3764,17 @@ facet_account(struct facet *facet)
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
@@ -3939,7 +3949,7 @@ facet_check_consistency(struct facet *facet)
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) {
@@ -4049,7 +4059,7 @@ facet_revalidate(struct facet *facet)
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;
@@ -4179,7 +4189,8 @@ flow_push_stats(struct rule_dpif *rule,
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);
}
/* Subfacets. */
@@ -4339,7 +4350,7 @@ subfacet_make_actions(struct subfacet *subfacet, const struct ofpbuf *packet,
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;
@@ -4576,8 +4587,8 @@ rule_construct(struct rule *rule_)
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;
}
@@ -4669,7 +4680,7 @@ rule_execute(struct rule *rule_, const struct flow *flow,
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);
@@ -4686,8 +4697,8 @@ rule_modify_actions(struct rule *rule_)
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;
@@ -4740,8 +4751,8 @@ send_packet(const struct ofport_dpif *ofport, struct ofpbuf *packet)
/* 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'.
@@ -4987,7 +4998,7 @@ xlate_table_action(struct action_xlate_ctx *ctx,
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--;
}
@@ -5003,16 +5014,21 @@ xlate_table_action(struct action_xlate_ctx *ctx,
}
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);
}
@@ -5125,8 +5141,8 @@ compose_dec_ttl(struct action_xlate_ctx *ctx)
}
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;
@@ -5173,44 +5189,32 @@ xlate_output_action__(struct action_xlate_ctx *ctx,
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) {
@@ -5232,21 +5236,16 @@ xlate_enqueue_action(struct action_xlate_ctx *ctx,
}
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 {
@@ -5256,9 +5255,9 @@ 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) {
@@ -5271,7 +5270,7 @@ xlate_autopath(struct action_xlate_ctx *ctx,
ofp_port = slave->up.ofp_port;
}
}
- autopath_execute(naa, &ctx->flow, ofp_port);
+ nxm_reg_load(&ap->dst, ofp_port, &ctx->flow);
}
static bool
@@ -5297,14 +5296,31 @@ slave_enabled_cb(uint16_t ofp_port, void *ofproto_)
}
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)) {
@@ -5312,7 +5328,7 @@ xlate_learn_action(struct action_xlate_ctx *ctx,
ofperr_get_name(error));
}
- free(fm.actions);
+ ofpbuf_uninit(&ofpacts);
}
/* Reduces '*timeout' to no more than 'max'. A value of zero in either case
@@ -5327,13 +5343,13 @@ reduce_timeout(uint16_t max, uint16_t *timeout)
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);
}
}
@@ -5359,13 +5375,12 @@ may_receive(const struct ofport_dpif *port, struct action_xlate_ctx *ctx)
}
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)) {
@@ -5378,184 +5393,146 @@ do_xlate_actions(const union ofp_action *in, size_t n_in,
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;
}
}
@@ -5591,11 +5568,11 @@ action_xlate_ctx_init(struct action_xlate_ctx *ctx,
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
@@ -5665,7 +5642,7 @@ xlate_actions(struct action_xlate_ctx *ctx,
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) {
@@ -5700,17 +5677,18 @@ xlate_actions(struct action_xlate_ctx *ctx,
}
}
-/* 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);
}
@@ -6371,7 +6349,7 @@ set_frag_handling(struct ofproto *ofproto_,
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;
@@ -6380,8 +6358,7 @@ packet_out(struct ofproto *ofproto_, struct ofpbuf *packet,
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;
@@ -6403,7 +6380,7 @@ packet_out(struct ofproto *ofproto_, struct ofpbuf *packet,
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);
@@ -6560,7 +6537,7 @@ trace_format_rule(struct ds *result, uint8_t table_id, int level,
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');
}
@@ -6768,7 +6745,7 @@ ofproto_trace(struct ofproto_dpif *ofproto, const struct flow *flow,
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');
diff --git a/ofproto/ofproto-provider.h b/ofproto/ofproto-provider.h
index 2cbb9ae0..afd17a60 100644
--- a/ofproto/ofproto-provider.h
+++ b/ofproto/ofproto-provider.h
@@ -29,6 +29,7 @@
#include "shash.h"
#include "timeval.h"
+struct ofpact;
struct ofputil_flow_mod;
struct simap;
@@ -184,8 +185,8 @@ struct rule {
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 *
@@ -781,13 +782,11 @@ struct ofproto_class {
* 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.
@@ -918,14 +917,13 @@ struct ofproto_class {
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
@@ -957,8 +955,8 @@ struct ofproto_class {
* 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 ## */
@@ -1195,7 +1193,7 @@ BUILD_ASSERT_DECL(OFPROTO_POSTPONE < OFPERR_OFS);
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 *);
diff --git a/ofproto/ofproto.c b/ofproto/ofproto.c
index ce4da9d3..b23c79b8 100644
--- a/ofproto/ofproto.c
+++ b/ofproto/ofproto.c
@@ -32,6 +32,7 @@
#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"
@@ -118,8 +119,8 @@ struct ofoperation {
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. */
};
@@ -1378,27 +1379,28 @@ ofproto_port_del(struct ofproto *ofproto, uint16_t ofp_port)
* (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);
}
}
@@ -1857,7 +1859,7 @@ static void
ofproto_rule_destroy__(struct rule *rule)
{
if (rule) {
- free(rule->actions);
+ free(rule->ofpacts);
rule->ofproto->ofproto_class->rule_dealloc(rule);
}
}
@@ -1879,23 +1881,12 @@ ofproto_rule_destroy(struct rule *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
@@ -2052,6 +2043,8 @@ handle_packet_out(struct ofconn *ofconn, const struct ofp_packet_out *opo)
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;
@@ -2059,20 +2052,21 @@ handle_packet_out(struct ofconn *ofconn, const struct ofp_packet_out *opo)
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);
@@ -2082,9 +2076,12 @@ handle_packet_out(struct ofconn *ofconn, const struct ofp_packet_out *opo)
/* 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;
}
@@ -2486,8 +2483,8 @@ handle_flow_stats_request(struct ofconn *ofconn,
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);
@@ -2513,8 +2510,8 @@ flow_stats_ds(struct rule *rule, struct ds *results)
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");
}
@@ -2771,6 +2768,9 @@ is_flow_deletion_pending(const struct ofproto *ofproto,
* 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
@@ -2839,8 +2839,8 @@ add_flow(struct ofproto *ofproto, struct ofconn *ofconn,
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;
@@ -2922,14 +2922,14 @@ modify_flows__(struct ofproto *ofproto, struct ofconn *ofconn,
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();
}
@@ -3127,30 +3127,35 @@ handle_flow_mod(struct ofconn *ofconn, const struct ofp_header *oh)
{
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. */
@@ -3179,7 +3184,10 @@ handle_flow_mod(struct ofconn *ofconn, const struct ofp_header *oh)
}
ofproto->last_op = now;
- return 0;
+exit_free_ofpacts:
+ ofpbuf_uninit(&ofpacts);
+exit:
+ return error;
}
static enum ofperr
@@ -3615,7 +3623,7 @@ ofoperation_destroy(struct ofoperation *op)
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)) {
@@ -3715,10 +3723,10 @@ ofoperation_complete(struct ofoperation *op, enum ofperr error)
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;
diff --git a/tests/automake.mk b/tests/automake.mk
index 81d29427..3e42a3ec 100644
--- a/tests/automake.mk
+++ b/tests/automake.mk
@@ -14,6 +14,7 @@ TESTSUITE_AT = \
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 \
diff --git a/tests/learn.at b/tests/learn.at
index 9304861b..da82f51f 100644
--- a/tests/learn.at
+++ b/tests/learn.at
@@ -10,7 +10,7 @@ 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)
-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
@@ -42,7 +42,7 @@ ip,actions=learn(eth_type=0x800,OXM_OF_IPV4_DST[])
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[])
]])
diff --git a/tests/ofp-actions.at b/tests/ofp-actions.at
new file mode 100644
index 00000000..6f702136
--- /dev/null
+++ b/tests/ofp-actions.at
@@ -0,0 +1,121 @@
+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
diff --git a/tests/test-bundle.c b/tests/test-bundle.c
index 672c426e..f2d9b824 100644
--- a/tests/test-bundle.c
+++ b/tests/test-bundle.c
@@ -1,4 +1,4 @@
-/* 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.
@@ -21,6 +21,7 @@
#include <stdlib.h>
#include "flow.h"
+#include "ofp-actions.h"
#include "ofpbuf.h"
#include "random.h"
@@ -64,22 +65,24 @@ slave_enabled_cb(uint16_t slave_id, void *aux)
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 *
@@ -101,7 +104,7 @@ int
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;
@@ -114,12 +117,12 @@ main(int argc, char *argv[])
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. ");
@@ -136,10 +139,6 @@ main(int argc, char *argv[])
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
@@ -188,23 +187,19 @@ main(int argc, char *argv[])
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) {
@@ -229,7 +224,7 @@ main(int argc, char *argv[])
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;
@@ -262,7 +257,7 @@ main(int argc, char *argv[])
old_n_enabled = n_enabled;
}
- free(nab);
+ free(bundle);
free(flows);
return ok ? 0 : 1;
}
diff --git a/tests/test-multipath.c b/tests/test-multipath.c
index 483eb3db..8a355677 100644
--- a/tests/test-multipath.c
+++ b/tests/test-multipath.c
@@ -1,5 +1,5 @@
/*
- * 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.
@@ -25,6 +25,7 @@
#include <stdlib.h>
#include "flow.h"
+#include "ofp-actions.h"
#include "random.h"
#include "util.h"
@@ -32,7 +33,7 @@ int
main(int argc, char *argv[])
{
enum { MP_MAX_LINKS = 63 };
- struct nx_action_multipath mp;
+ struct ofpact_multipath mp;
bool ok = true;
int n;
@@ -60,11 +61,11 @@ main(int argc, char *argv[])
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];
@@ -91,7 +92,7 @@ main(int argc, char *argv[])
"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",
diff --git a/tests/testsuite.at b/tests/testsuite.at
index 9796b036..3ec28d62 100644
--- a/tests/testsuite.at
+++ b/tests/testsuite.at
@@ -56,6 +56,7 @@ m4_include([tests/classifier.at])
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])
diff --git a/utilities/ovs-ofctl.c b/utilities/ovs-ofctl.c
index dbc46cd4..b95f5da5 100644
--- a/utilities/ovs-ofctl.c
+++ b/utilities/ovs-ofctl.c
@@ -35,9 +35,9 @@
#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"
@@ -871,7 +871,7 @@ do_flow_mod__(const char *remote, struct ofputil_flow_mod *fms, size_t n_fms)
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);
}
@@ -1243,19 +1243,19 @@ static void
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++) {
@@ -1274,7 +1274,7 @@ do_packet_out(int argc, char *argv[])
ofpbuf_delete(packet);
}
vconn_close(vconn);
- ofpbuf_uninit(&actions);
+ ofpbuf_uninit(&ofpacts);
}
static void
@@ -1492,8 +1492,8 @@ struct fte_version {
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. */
@@ -1501,7 +1501,7 @@ static void
fte_version_free(struct fte_version *version)
{
if (version) {
- free(version->actions);
+ free(version->ofpacts);
free(version);
}
}
@@ -1516,9 +1516,8 @@ fte_version_equals(const struct fte_version *a, const struct fte_version *b)
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
@@ -1539,7 +1538,7 @@ fte_version_print(const struct fte_version *version)
}
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);
}
@@ -1627,8 +1626,8 @@ read_flows_from_file(const char *filename, struct classifier *cls, int index)
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);
@@ -1694,10 +1693,14 @@ read_flows_from_switch(struct vconn *vconn,
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");
}
@@ -1709,9 +1712,8 @@ read_flows_from_switch(struct vconn *vconn,
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);
}
@@ -1744,11 +1746,11 @@ fte_make_flow_mod(const struct fte *fte, int index, uint16_t command,
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);
@@ -1901,7 +1903,7 @@ do_parse_flows__(struct ofputil_flow_mod *fms, size_t n_fms)
ofp_print(stdout, msg->data, msg->size, verbosity);
ofpbuf_delete(msg);
- free(fm->actions);
+ free(fm->ofpacts);
}
}
@@ -1996,6 +1998,84 @@ do_parse_oxm(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
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
@@ -2129,6 +2209,7 @@ static const struct command all_commands[] = {
{ "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 },