Commit f25d0cf3 authored by Ben Pfaff's avatar Ben Pfaff

Introduce ofpacts, an abstraction of OpenFlow actions.

OpenFlow actions have always been somewhat awkward to handle.
Moreover, over time we've started creating actions that require more
complicated parsing.  When we maintain those actions internally in
their wire format, we end up parsing them multiple times, whenever
we have to look at the set of actions.

When we add support for OpenFlow 1.1 or later protocols, the situation
will get worse, because these newer protocols support many of the same
actions but with different representations.  It becomes unrealistic to
handle each protocol in its wire format.

This commit adopts a new strategy, by converting OpenFlow actions into
an internal form from the wire format when they are read, and converting
them back to the wire format when flows are dumped.  I believe that this
will be more maintainable over time.

Thanks to Simon Horman and Pravin Shelar for reviews.
Signed-off-by: default avatarBen Pfaff <blp@nicira.com>
parent 690a61c5
......@@ -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
===========
......
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.
......
......@@ -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 \
......
/*
* 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);
}
/*
* 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 */
This diff is collapsed.
/* 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 */
/*
* 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 */
This diff is collapsed.
/*
* 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 */
......@@ -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));
}
}
......
......@@ -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;