mirror of
https://github.com/openvswitch/ovs
synced 2025-08-31 06:15:47 +00:00
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: Ben Pfaff <blp@nicira.com>
This commit is contained in:
526
lib/ofp-util.c
526
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
|
||||
|
Reference in New Issue
Block a user