Skip to content

Commit

Permalink
vswitch: Implement bundle action.
Browse files Browse the repository at this point in the history
This patch creates a new action called "bundle".  Bundles are a way
to implement a simple form of multipath in OpenFlow by grouping
several ports in a single output-like action.
  • Loading branch information
ejj committed Jul 19, 2011
1 parent 7741013 commit daff335
Show file tree
Hide file tree
Showing 16 changed files with 834 additions and 2 deletions.
74 changes: 73 additions & 1 deletion include/openflow/nicira-ext.h
Original file line number Diff line number Diff line change
Expand Up @@ -277,7 +277,8 @@ enum nx_action_subtype {
NXAST_NOTE, /* struct nx_action_note */
NXAST_SET_TUNNEL64, /* struct nx_action_set_tunnel64 */
NXAST_MULTIPATH, /* struct nx_action_multipath */
NXAST_AUTOPATH /* struct nx_action_autopath */
NXAST_AUTOPATH, /* struct nx_action_autopath */
NXAST_BUNDLE /* struct nx_action_bundle */
};

/* Header for Nicira-defined actions. */
Expand Down Expand Up @@ -665,6 +666,77 @@ struct nx_action_autopath {
};
OFP_ASSERT(sizeof(struct nx_action_autopath) == 24);

/* Action structure for NXAST_BUNDLE.
*
* NXAST_BUNDLE chooses a slave from a supplied list of options, and outputs to
* its selection.
*
* The list of possible slaves follows the nx_action_bundle structure. The size
* of each slave is governed by its type as indicated by the 'slave_type'
* parameter. The list of slaves should be padded at its end with zeros to make
* the total length of the action a multiple of 8.
*
* Switches infer from the 'slave_type' parameter the size of each slave. All
* implementations must support the NXM_OF_IN_PORT 'slave_type' which indicates
* that the slaves are OpenFlow port numbers with NXM_LENGTH(NXM_OF_IN_PORT) ==
* 2 byte width. Switches should reject actions which indicate unknown or
* unsupported slave types.
*
* Switches use a strategy dictated by the 'algorithm' parameter to choose a
* slave. If the switch does not support the specified 'algorithm' parameter,
* it should reject the action.
*
* Some slave selection strategies require the use of a hash function, in which
* case the 'fields' and 'basis' parameters should be populated. The 'fields'
* parameter (one of NX_HASH_FIELDS_*) designates which parts of the flow to
* hash. Refer to the definition of "enum nx_hash_fields" for details. The
* 'basis' parameter is used as a universal hash parameter. Different values
* of 'basis' yield different hash results.
*
* The 'zero' parameter at the end of the action structure is reserved for
* future use. Switches are required to reject actions which have nonzero
* bytes in the 'zero' field. */
struct nx_action_bundle {
ovs_be16 type; /* OFPAT_VENDOR. */
ovs_be16 len; /* Length including slaves. */
ovs_be32 vendor; /* NX_VENDOR_ID. */
ovs_be16 subtype; /* NXAST_BUNDLE. */

/* Slave choice algorithm to apply to hash value. */
ovs_be16 algorithm; /* One of NX_BD_ALG_*. */

/* What fields to hash and how. */
ovs_be16 fields; /* One of NX_BD_FIELDS_*. */
ovs_be16 basis; /* Universal hash parameter. */

ovs_be32 slave_type; /* NXM_OF_IN_PORT. */
ovs_be16 n_slaves; /* Number of slaves. */

uint8_t zero[10]; /* Reserved. Must be zero. */
};
OFP_ASSERT(sizeof(struct nx_action_bundle) == 32);

/* NXAST_BUNDLE: Bundle slave choice algorithm to apply.
*
* In the descriptions below, 'slaves' is the list of possible slaves in the
* order they appear in the OpenFlow action. */
enum nx_bd_algorithm {
/* Chooses the first live slave listed in the bundle.
*
* O(n_slaves) performance. */
NX_BD_ALG_ACTIVE_BACKUP,

/* for i in [0,n_slaves):
* weights[i] = hash(flow, i)
* slave = { slaves[i] such that weights[i] >= weights[j] for all j != i }
*
* Redistributes 1/n_slaves of traffic when a slave's liveness changes.
* O(n_slaves) performance.
*
* Uses the 'fields' and 'basis' parameters. */
NX_BD_ALG_HRW /* Highest Random Weight. */
};

/* Flexible flow specifications (aka NXM = Nicira Extended Match).
*
* OpenFlow 1.0 has "struct ofp_match" for specifying flow matches. This
Expand Down
2 changes: 2 additions & 0 deletions lib/automake.mk
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ lib_libopenvswitch_a_SOURCES = \
lib/bitmap.h \
lib/bond.c \
lib/bond.h \
lib/bundle.c \
lib/bundle.h \
lib/byte-order.h \
lib/byteq.c \
lib/byteq.h \
Expand Down
256 changes: 256 additions & 0 deletions lib/bundle.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,256 @@
/* Copyright (c) 2011 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 "bundle.h"

#include <arpa/inet.h>
#include <inttypes.h>

#include "dynamic-string.h"
#include "multipath.h"
#include "nx-match.h"
#include "ofpbuf.h"
#include "ofp-util.h"
#include "openflow/nicira-ext.h"
#include "vlog.h"

#define BUNDLE_MAX_SLAVES 2048

VLOG_DEFINE_THIS_MODULE(bundle);

/* Executes 'nab' 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,
bool (*slave_enabled)(uint16_t ofp_port, void *aux), void *aux)
{
uint32_t flow_hash, best_hash;
int best, i;

assert(nab->algorithm == htons(NX_BD_ALG_HRW));

flow_hash = flow_hash_fields(flow, ntohs(nab->fields), ntohs(nab->basis));
best = -1;

for (i = 0; i < ntohs(nab->n_slaves); i++) {
if (slave_enabled(bundle_get_slave(nab, i), aux)) {
uint32_t hash = hash_2words(i, flow_hash);

if (best < 0 || hash > best_hash) {
best_hash = hash;
best = i;
}
}
}

return best >= 0 ? bundle_get_slave(nab, best) : OFPP_NONE;
}

/* Checks that 'nab' specifies a bundle action which is supported by this
* bundle module. Uses the 'max_ports' parameter to validate each port using
* ofputil_check_output_port(). Returns 0 if 'nab' is supported, otherwise an
* OpenFlow error code (as returned by ofp_mkerr()). */
int
bundle_check(const struct nx_action_bundle *nab, int max_ports)
{
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
uint16_t n_slaves, fields, algorithm, slave_type, subtype;
size_t slaves_size, i;
int error;

subtype = ntohs(nab->subtype);
n_slaves = ntohs(nab->n_slaves);
fields = ntohs(nab->fields);
algorithm = ntohs(nab->algorithm);
slave_type = ntohs(nab->slave_type);
slaves_size = ntohs(nab->len) - sizeof *nab;

error = ofp_mkerr(OFPET_BAD_ACTION, OFPBAC_BAD_ARGUMENT);
if (!flow_hash_fields_valid(fields)) {
VLOG_WARN_RL(&rl, "unsupported fields %"PRIu16, fields);
} else if (n_slaves > BUNDLE_MAX_SLAVES) {
VLOG_WARN_RL(&rl, "too may slaves");
} else if (algorithm != NX_BD_ALG_HRW) {
VLOG_WARN_RL(&rl, "unsupported algorithm %"PRIu16, 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 = ofp_mkerr(OFPET_BAD_ACTION, OFPBAC_BAD_ARGUMENT);
}
}

if (slaves_size < 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);
error = ofp_mkerr(OFPET_BAD_ACTION, OFPBAC_BAD_LEN);
}

for (i = 0; i < n_slaves; i++) {
uint16_t ofp_port = bundle_get_slave(nab, i);
int ofputil_error = ofputil_check_output_port(ofp_port, max_ports);

if (ofputil_error) {
VLOG_WARN_RL(&rl, "invalid slave %"PRIu16, ofp_port);
error = ofputil_error;
}

/* Controller slaves are unsupported due to the lack of a max_len
* argument. This may or may not change in the future. There doesn't
* seem to be a real-world use-case for supporting it. */
if (ofp_port == OFPP_CONTROLLER) {
VLOG_WARN_RL(&rl, "unsupported controller slave");
error = ofp_mkerr(OFPET_BAD_ACTION, OFPBAC_BAD_OUT_PORT);
}
}

return error;
}

/* 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)
{
char *fields, *basis, *algorithm, *slave_type, *slave_delim;
struct nx_action_bundle *nab;
char *tokstr, *save_ptr;
uint16_t n_slaves;

save_ptr = NULL;
tokstr = xstrdup(s);
fields = strtok_r(tokstr, ", ", &save_ptr);
basis = strtok_r(NULL, ", ", &save_ptr);
algorithm = strtok_r(NULL, ", ", &save_ptr);
slave_type = strtok_r(NULL, ", ", &save_ptr);
slave_delim = strtok_r(NULL, ": ", &save_ptr);

if (!slave_delim) {
ovs_fatal(0, "%s: not enough arguments to bundle action", s);
}

if (strcasecmp(slave_delim, "slaves")) {
ovs_fatal(0, "%s: missing slave delimiter, expected `slaves' got `%s'",
s, slave_delim);
}

b->l2 = ofpbuf_put_zeros(b, sizeof *nab);

n_slaves = 0;
for (;;) {
ovs_be16 slave_be;
char *slave;

slave = strtok_r(NULL, ", ", &save_ptr);
if (!slave || n_slaves >= BUNDLE_MAX_SLAVES) {
break;
}

slave_be = htons(atoi(slave));
ofpbuf_put(b, &slave_be, sizeof slave_be);

n_slaves++;
}

/* 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->type = htons(OFPAT_VENDOR);
nab->len = htons(b->size - ((char *) b->l2 - (char *) b->data));
nab->vendor = htonl(NX_VENDOR_ID);
nab->subtype = htons(NXAST_BUNDLE);
nab->n_slaves = htons(n_slaves);
nab->basis = htons(atoi(basis));

if (!strcasecmp(fields, "eth_src")) {
nab->fields = htons(NX_HASH_FIELDS_ETH_SRC);
} else if (!strcasecmp(fields, "symmetric_l4")) {
nab->fields = htons(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);
} else if (!strcasecmp(algorithm, "hrw")) {
nab->algorithm = htons(NX_BD_ALG_HRW);
} else {
ovs_fatal(0, "%s: unknown algorithm `%s'", s, algorithm);
}

if (!strcasecmp(slave_type, "ofport")) {
nab->slave_type = htons(NXM_OF_IN_PORT);
} else {
ovs_fatal(0, "%s: unknown slave_type `%s'", s, slave_type);
}

b->l2 = NULL;
free(tokstr);
}

/* Appends a human-readable representation of 'nab' to 's'. */
void
bundle_format(const struct nx_action_bundle *nab, struct ds *s)
{
const char *fields, *algorithm, *slave_type;
size_t i;

fields = flow_hash_fields_to_str(ntohs(nab->fields));

switch (ntohs(nab->algorithm)) {
case NX_BD_ALG_HRW:
algorithm = "hrw";
break;
case NX_BD_ALG_ACTIVE_BACKUP:
algorithm = "active_backup";
break;
default:
algorithm = "<unknown>";
}

switch (ntohs(nab->slave_type)) {
case NXM_OF_IN_PORT:
slave_type = "ofport";
break;
default:
slave_type = "<unknown>";
}

ds_put_format(s, "bundle(%s,%"PRIu16",%s,%s,slaves:", fields,
ntohs(nab->basis), algorithm, slave_type);

for (i = 0; i < ntohs(nab->n_slaves); i++) {
if (i) {
ds_put_cstr(s, ",");
}

ds_put_format(s, "%"PRIu16, bundle_get_slave(nab, i));
}

ds_put_cstr(s, ")");
}
Loading

0 comments on commit daff335

Please sign in to comment.