#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"
}
/* Given the IP netmask 'netmask', returns the number of bits of the IP address
- * that it wildcards, that is, the number of 0-bits in 'netmask'. 'netmask'
- * must be a CIDR netmask (see ip_is_cidr()). */
+ * that it wildcards, that is, the number of 0-bits in 'netmask', a number
+ * between 0 and 32 inclusive.
+ *
+ * If 'netmask' is not a CIDR netmask (see ip_is_cidr()), the return value will
+ * still be in the valid range but isn't otherwise meaningful. */
int
ofputil_netmask_to_wcbits(ovs_be32 netmask)
{
void
ofputil_wildcard_from_ofpfw10(uint32_t ofpfw, struct flow_wildcards *wc)
{
- BUILD_ASSERT_DECL(FLOW_WC_SEQ == 11);
+ BUILD_ASSERT_DECL(FLOW_WC_SEQ == 12);
/* Initialize most of rule->wc. */
flow_wildcards_init_catchall(wc);
rule->flow.nw_proto = match->nw_proto;
/* Translate VLANs. */
- if (!(ofpfw & OFPFW10_DL_VLAN) && match->dl_vlan == htons(OFP_VLAN_NONE)) {
+ if (!(ofpfw & OFPFW10_DL_VLAN) &&
+ match->dl_vlan == htons(OFP10_VLAN_NONE)) {
/* Match only packets without 802.1Q header.
*
* When OFPFW10_DL_VLAN_PCP is wildcarded, this is obviously correct.
ofpfw |= OFPFW10_DL_VLAN | OFPFW10_DL_VLAN_PCP;
} else if (rule->wc.vlan_tci_mask & htons(VLAN_CFI)
&& !(rule->flow.vlan_tci & htons(VLAN_CFI))) {
- match->dl_vlan = htons(OFP_VLAN_NONE);
+ match->dl_vlan = htons(OFP10_VLAN_NONE);
} else {
if (!(rule->wc.vlan_tci_mask & htons(VLAN_VID_MASK))) {
ofpfw |= OFPFW10_DL_VLAN;
if (!(wc & OFPFW11_NW_PROTO)) {
cls_rule_set_nw_proto(rule, match->nw_proto);
}
-
- if (!ip_is_cidr(~match->nw_src_mask) ||
- !ip_is_cidr(~match->nw_dst_mask)) {
- return OFPERR_OFPBMC_BAD_NW_ADDR_MASK;
- }
cls_rule_set_nw_src_masked(rule, match->nw_src, ~match->nw_src_mask);
cls_rule_set_nw_dst_masked(rule, match->nw_dst, ~match->nw_dst_mask);
}
}
if (match->metadata_mask != htonll(UINT64_MAX)) {
- /* Metadata field not yet supported because we haven't decided how to
- * map it onto our existing fields (or whether to add a new field). */
- return OFPERR_OFPBMC_BAD_FIELD;
+ cls_rule_set_metadata_masked(rule, match->metadata,
+ ~match->metadata_mask);
}
return 0;
wc |= OFPFW11_MPLS_LABEL;
wc |= OFPFW11_MPLS_TC;
- /* Metadata field not yet supported */
- match->metadata_mask = htonll(UINT64_MAX);
+ match->metadata = rule->flow.metadata;
+ match->metadata_mask = ~rule->wc.metadata_mask;
match->wildcards = htonl(wc);
}
sizeof(struct ofp_port_status) + sizeof(struct ofp11_port), 0 },
{ OFPUTIL_OFPT_PACKET_OUT, OFP10_VERSION,
- OFPT10_PACKET_OUT, "OFPT_PACKET_OUT",
+ OFPT_PACKET_OUT, "OFPT_PACKET_OUT",
sizeof(struct ofp_packet_out), 1 },
{ OFPUTIL_OFPT_FLOW_MOD, OFP10_VERSION,
- OFPT10_FLOW_MOD, "OFPT_FLOW_MOD",
+ OFPT_FLOW_MOD, "OFPT_FLOW_MOD",
sizeof(struct ofp_flow_mod), 1 },
{ OFPUTIL_OFPT_PORT_MOD, OFP10_VERSION,
{
const struct flow_wildcards *wc = &rule->wc;
- BUILD_ASSERT_DECL(FLOW_WC_SEQ == 11);
+ BUILD_ASSERT_DECL(FLOW_WC_SEQ == 12);
/* NXM and OF1.1+ supports bitwise matching on ethernet addresses. */
if (!eth_mask_is_exact(wc->dl_src_mask)
return OFPUTIL_P_NXM_ANY;
}
+ /* NXM and OF1.1+ support matching metadata. */
+ if (wc->metadata_mask != htonll(0)) {
+ return OFPUTIL_P_NXM_ANY;
+ }
+
/* Only NXM supports matching ARP hardware addresses. */
if (!(wc->wildcards & FWW_ARP_SHA) || !(wc->wildcards & FWW_ARP_THA)) {
return OFPUTIL_P_NXM_ANY;
return OFPUTIL_P_NXM_ANY;
}
+ /* Only NXM supports non-CIDR IPv4 address masks. */
+ if (!ip_is_cidr(wc->nw_src_mask) || !ip_is_cidr(wc->nw_dst_mask)) {
+ return OFPUTIL_P_NXM_ANY;
+ }
+
/* Only NXM supports bitwise matching on transport port. */
if ((wc->tp_src_mask && wc->tp_src_mask != htons(UINT16_MAX)) ||
(wc->tp_dst_mask && wc->tp_dst_mask != htons(UINT16_MAX))) {
* flow_mod in 'fm'. Returns 0 if successful, otherwise an OpenFlow error
* code.
*
- * Does not validate the flow_mod actions. */
+ * Uses 'ofpacts' to store the abstract OFPACT_* version of 'oh''s actions.
+ * The caller must initialize 'ofpacts' and retains ownership of it.
+ * 'fm->ofpacts' will point into the 'ofpacts' buffer.
+ *
+ * Does not validate the flow_mod actions. The caller should do that, with
+ * ofpacts_check(). */
enum ofperr
ofputil_decode_flow_mod(struct ofputil_flow_mod *fm,
const struct ofp_header *oh,
- enum ofputil_protocol protocol)
+ enum ofputil_protocol protocol,
+ struct ofpbuf *ofpacts)
{
const struct ofputil_msg_type *type;
uint16_t command;
uint16_t priority;
enum ofperr error;
- /* Dissect the message. */
+ /* Get the ofp_flow_mod. */
ofm = ofpbuf_pull(&b, sizeof *ofm);
- error = ofputil_pull_actions(&b, b.size, &fm->actions, &fm->n_actions);
- if (error) {
- return error;
- }
/* Set priority based on original wildcards. Normally we'd allow
* ofputil_cls_rule_from_match() to do this for us, but
ofputil_cls_rule_from_ofp10_match(&ofm->match, priority, &fm->cr);
ofputil_normalize_rule(&fm->cr);
+ /* Now get the actions. */
+ error = ofpacts_pull_openflow10(&b, b.size, ofpacts);
+ if (error) {
+ return error;
+ }
+
/* Translate the message. */
command = ntohs(ofm->command);
fm->cookie = htonll(0);
if (error) {
return error;
}
- error = ofputil_pull_actions(&b, b.size, &fm->actions, &fm->n_actions);
+ error = ofpacts_pull_openflow10(&b, b.size, ofpacts);
if (error) {
return error;
}
NOT_REACHED();
}
+ fm->ofpacts = ofpacts->data;
+ fm->ofpacts_len = ofpacts->size;
if (protocol & OFPUTIL_P_TID) {
fm->command = command & 0xff;
fm->table_id = command >> 8;
ofputil_encode_flow_mod(const struct ofputil_flow_mod *fm,
enum ofputil_protocol protocol)
{
- size_t actions_len = fm->n_actions * sizeof *fm->actions;
struct ofp_flow_mod *ofm;
struct nx_flow_mod *nfm;
struct ofpbuf *msg;
switch (protocol) {
case OFPUTIL_P_OF10:
case OFPUTIL_P_OF10_TID:
- msg = ofpbuf_new(sizeof *ofm + actions_len);
- ofm = put_openflow(sizeof *ofm, OFPT10_FLOW_MOD, msg);
+ 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;
ofm->command = htons(command);
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);
nfm->cookie = fm->new_cookie;
match_len = nx_put_match(msg, false, &fm->cr,
fm->cookie, fm->cookie_mask);
+ nfm = msg->data;
nfm->idle_timeout = htons(fm->idle_timeout);
nfm->hard_timeout = htons(fm->hard_timeout);
nfm->priority = htons(fm->cr.priority);
NOT_REACHED();
}
- ofpbuf_put(msg, fm->actions, actions_len);
+ if (fm->ofpacts) {
+ ofpacts_put_openflow10(fm->ofpacts, fm->ofpacts_len, msg);
+ }
update_openflow_length(msg);
return msg;
}
* 'flow_age_extension' as true so that the contents of 'msg' determine the
* 'idle_age' and 'hard_age' members in 'fs'.
*
+ * Uses 'ofpacts' to store the abstract OFPACT_* version of the flow stats
+ * reply's actions. The caller must initialize 'ofpacts' and retains ownership
+ * of it. 'fs->ofpacts' will point into the 'ofpacts' buffer.
+ *
* Returns 0 if successful, EOF if no replies were left in this 'msg',
* otherwise a positive errno value. */
int
ofputil_decode_flow_stats_reply(struct ofputil_flow_stats *fs,
struct ofpbuf *msg,
- bool flow_age_extension)
+ bool flow_age_extension,
+ struct ofpbuf *ofpacts)
{
const struct ofputil_msg_type *type;
int code;
return EINVAL;
}
- if (ofputil_pull_actions(msg, length - sizeof *ofs,
- &fs->actions, &fs->n_actions)) {
+ if (ofpacts_pull_openflow10(msg, length - sizeof *ofs, ofpacts)) {
return EINVAL;
}
fs->byte_count = ntohll(get_32aligned_be64(&ofs->byte_count));
} else if (code == OFPUTIL_NXST_FLOW_REPLY) {
const struct nx_flow_stats *nfs;
- size_t match_len, length;
+ size_t match_len, actions_len, length;
nfs = ofpbuf_try_pull(msg, sizeof *nfs);
if (!nfs) {
return EINVAL;
}
- if (ofputil_pull_actions(msg,
- length - sizeof *nfs - ROUND_UP(match_len, 8),
- &fs->actions, &fs->n_actions)) {
+ actions_len = length - sizeof *nfs - ROUND_UP(match_len, 8);
+ if (ofpacts_pull_openflow10(msg, actions_len, ofpacts)) {
return EINVAL;
}
NOT_REACHED();
}
+ fs->ofpacts = ofpacts->data;
+ fs->ofpacts_len = ofpacts->size;
+
return 0;
}
ofputil_append_flow_stats_reply(const struct ofputil_flow_stats *fs,
struct list *replies)
{
- size_t act_len = fs->n_actions * sizeof *fs->actions;
- const struct ofp_stats_msg *osm;
+ struct ofpbuf *reply = ofpbuf_from_list(list_back(replies));
+ const struct ofp_stats_msg *osm = reply->data;
+ size_t start_ofs = reply->size;
- osm = ofpbuf_from_list(list_back(replies))->data;
if (osm->type == htons(OFPST_FLOW)) {
- size_t len = offsetof(struct ofp_flow_stats, actions) + act_len;
struct ofp_flow_stats *ofs;
- ofs = ofputil_append_stats_reply(len, replies);
- ofs->length = htons(len);
+ ofpbuf_put_uninit(reply, sizeof *ofs);
+ ofpacts_put_openflow10(fs->ofpacts, fs->ofpacts_len, reply);
+
+ ofs = ofpbuf_at_assert(reply, start_ofs, sizeof *ofs);
+ ofs->length = htons(reply->size - start_ofs);
ofs->table_id = fs->table_id;
ofs->pad = 0;
ofputil_cls_rule_to_ofp10_match(&fs->rule, &ofs->match);
htonll(unknown_to_zero(fs->packet_count)));
put_32aligned_be64(&ofs->byte_count,
htonll(unknown_to_zero(fs->byte_count)));
- memcpy(ofs->actions, fs->actions, act_len);
} else if (osm->type == htons(OFPST_VENDOR)) {
struct nx_flow_stats *nfs;
- struct ofpbuf *msg;
- size_t start_len;
+ int match_len;
- msg = ofputil_reserve_stats_reply(
- sizeof *nfs + NXM_MAX_LEN + act_len, replies);
- start_len = msg->size;
+ ofpbuf_put_uninit(reply, sizeof *nfs);
+ match_len = nx_put_match(reply, false, &fs->rule, 0, 0);
+ ofpacts_put_openflow10(fs->ofpacts, fs->ofpacts_len, reply);
- nfs = ofpbuf_put_uninit(msg, sizeof *nfs);
+ nfs = ofpbuf_at_assert(reply, start_ofs, sizeof *nfs);
+ nfs->length = htons(reply->size - start_ofs);
nfs->table_id = fs->table_id;
nfs->pad = 0;
nfs->duration_sec = htonl(fs->duration_sec);
nfs->hard_age = htons(fs->hard_age < 0 ? 0
: fs->hard_age < UINT16_MAX ? fs->hard_age + 1
: UINT16_MAX);
- nfs->match_len = htons(nx_put_match(msg, false, &fs->rule, 0, 0));
+ nfs->match_len = htons(match_len);
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);
} else {
NOT_REACHED();
}
+
+ ofputil_postappend_stats_reply(start_ofs, replies);
}
/* Converts abstract ofputil_aggregate_stats 'stats' into an OFPST_AGGREGATE or
return msg;
}
-int
+enum ofperr
ofputil_decode_packet_in(struct ofputil_packet_in *pin,
const struct ofp_header *oh)
{
pin->fmd.tun_id = rule.flow.tun_id;
pin->fmd.tun_id_mask = rule.wc.tun_id_mask;
+ pin->fmd.metadata = rule.flow.metadata;
+ pin->fmd.metadata_mask = rule.wc.metadata_mask;
+
memcpy(pin->fmd.regs, rule.flow.regs, sizeof pin->fmd.regs);
memcpy(pin->fmd.reg_masks, rule.wc.reg_masks,
sizeof pin->fmd.reg_masks);
cls_rule_init_catchall(&rule, 0);
cls_rule_set_tun_id_masked(&rule, pin->fmd.tun_id,
pin->fmd.tun_id_mask);
+ cls_rule_set_metadata_masked(&rule, pin->fmd.metadata,
+ pin->fmd.metadata_mask);
+
for (i = 0; i < FLOW_N_REGS; i++) {
cls_rule_set_reg_masked(&rule, i, pin->fmd.regs[i],
return false;
}
+/* Converts an OFPT_PACKET_OUT in 'opo' into an abstract ofputil_packet_out in
+ * 'po'.
+ *
+ * Uses 'ofpacts' to store the abstract OFPACT_* version of the packet out
+ * message's actions. The caller must initialize 'ofpacts' and retains
+ * ownership of it. 'po->ofpacts' will point into the 'ofpacts' buffer.
+ *
+ * Returns 0 if successful, otherwise an OFPERR_* value. */
enum ofperr
ofputil_decode_packet_out(struct ofputil_packet_out *po,
- const struct ofp_packet_out *opo)
+ const struct ofp_packet_out *opo,
+ struct ofpbuf *ofpacts)
{
enum ofperr error;
struct ofpbuf b;
ofpbuf_use_const(&b, opo, ntohs(opo->header.length));
ofpbuf_pull(&b, sizeof *opo);
- error = ofputil_pull_actions(&b, ntohs(opo->actions_len),
- &po->actions, &po->n_actions);
+ error = ofpacts_pull_openflow10(&b, ntohs(opo->actions_len), ofpacts);
if (error) {
return error;
}
+ po->ofpacts = ofpacts->data;
+ po->ofpacts_len = ofpacts->size;
if (po->buffer_id == UINT32_MAX) {
po->packet = b.data;
ofputil_encode_packet_out(const struct ofputil_packet_out *po)
{
struct ofp_packet_out *opo;
- size_t actions_len;
struct ofpbuf *msg;
size_t size;
- actions_len = po->n_actions * sizeof *po->actions;
- size = sizeof *opo + actions_len;
+ size = sizeof *opo + po->ofpacts_len;
if (po->buffer_id == UINT32_MAX) {
size += po->packet_len;
}
msg = ofpbuf_new(size);
- opo = put_openflow(sizeof *opo, OFPT10_PACKET_OUT, msg);
+ put_openflow(sizeof *opo, OFPT_PACKET_OUT, msg);
+ ofpacts_put_openflow10(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;
oh->length = htons(buffer->size);
}
-static void
-put_stats__(ovs_be32 xid, uint8_t ofp_type,
- ovs_be16 ofpst_type, ovs_be32 nxst_subtype,
- struct ofpbuf *msg)
+void
+ofputil_put_stats_header(ovs_be32 xid, uint8_t ofp_type,
+ ovs_be16 ofpst_type, ovs_be32 nxst_subtype,
+ struct ofpbuf *msg)
{
if (ofpst_type == htons(OFPST_VENDOR)) {
struct nicira_stats_msg *nsm;
struct ofpbuf *msg;
msg = *bufferp = ofpbuf_new(openflow_len);
- put_stats__(alloc_xid(), OFPT10_STATS_REQUEST,
- htons(ofpst_type), htonl(nxst_subtype), msg);
+ ofputil_put_stats_header(alloc_xid(), OFPT10_STATS_REQUEST,
+ htons(ofpst_type), htonl(nxst_subtype), msg);
ofpbuf_padto(msg, openflow_len);
return msg->data;
static void
put_stats_reply__(const struct ofp_stats_msg *request, struct ofpbuf *msg)
{
+ ovs_be32 nxst_subtype;
+
assert(request->header.type == OFPT10_STATS_REQUEST ||
request->header.type == OFPT10_STATS_REPLY);
- put_stats__(request->header.xid, OFPT10_STATS_REPLY, request->type,
- (request->type != htons(OFPST_VENDOR)
- ? htonl(0)
- : ((const struct nicira_stats_msg *) request)->subtype),
- msg);
+
+ nxst_subtype = (request->type != htons(OFPST_VENDOR)
+ ? htonl(0)
+ : ((const struct nicira_stats_msg *) request)->subtype);
+ ofputil_put_stats_header(request->header.xid, OFPT10_STATS_REPLY,
+ request->type, nxst_subtype, msg);
}
/* Creates a statistics reply message with total length 'openflow_len'
return ofpbuf_put_uninit(ofputil_reserve_stats_reply(len, replies), len);
}
+/* Sometimes, when composing stats replies, it's difficult to predict how long
+ * an individual reply chunk will be before actually encoding it into the reply
+ * buffer. This function allows easy handling of this case: just encode the
+ * reply, then use this function to break the message into two pieces if it
+ * exceeds the OpenFlow message limit.
+ *
+ * In detail, if the final stats message in 'replies' is too long for OpenFlow,
+ * this function breaks it into two separate stats replies, the first one with
+ * the first 'start_ofs' bytes, the second one containing the bytes from that
+ * offset onward. */
+void
+ofputil_postappend_stats_reply(size_t start_ofs, struct list *replies)
+{
+ struct ofpbuf *msg = ofpbuf_from_list(list_back(replies));
+
+ assert(start_ofs <= UINT16_MAX);
+ if (msg->size > UINT16_MAX) {
+ size_t len = msg->size - start_ofs;
+ memcpy(ofputil_append_stats_reply(len, replies),
+ (const uint8_t *) msg->data + start_ofs, len);
+ msg->size = start_ofs;
+ }
+}
+
/* Returns the first byte past the ofp_stats_msg header in 'oh'. */
const void *
ofputil_stats_body(const struct ofp_header *oh)
return ntohs(oh->length) - sizeof(struct nicira_stats_msg);
}
-struct ofpbuf *
-make_flow_mod(uint16_t command, const struct cls_rule *rule,
- size_t actions_len)
-{
- struct ofp_flow_mod *ofm;
- size_t size = sizeof *ofm + actions_len;
- struct ofpbuf *out = ofpbuf_new(size);
- ofm = ofpbuf_put_zeros(out, sizeof *ofm);
- ofm->header.version = OFP10_VERSION;
- ofm->header.type = OFPT10_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_del_flow(const struct cls_rule *rule)
-{
- struct ofpbuf *out = make_flow_mod(OFPFC_DELETE_STRICT, rule, 0);
- struct ofp_flow_mod *ofm = out->data;
- ofm->out_port = htons(OFPP_NONE);
- return out;
-}
-
-struct ofpbuf *
-make_add_simple_flow(const struct cls_rule *rule,
- uint32_t buffer_id, uint16_t out_port,
- uint16_t idle_timeout)
-{
- if (out_port != OFPP_NONE) {
- struct ofp_action_output *oao;
- struct ofpbuf *buffer;
-
- buffer = make_add_flow(rule, buffer_id, idle_timeout, sizeof *oao);
- ofputil_put_OFPAT10_OUTPUT(buffer)->port = htons(out_port);
- return buffer;
- } else {
- return make_add_flow(rule, buffer_id, idle_timeout, 0);
- }
-}
-
-struct ofpbuf *
-make_packet_in(uint32_t buffer_id, uint16_t in_port, uint8_t reason,
- const struct ofpbuf *payload, int max_send_len)
-{
- struct ofp_packet_in *opi;
- struct ofpbuf *buf;
- int send_len;
-
- send_len = MIN(max_send_len, payload->size);
- buf = ofpbuf_new(sizeof *opi + send_len);
- opi = put_openflow_xid(offsetof(struct ofp_packet_in, data),
- OFPT_PACKET_IN, 0, buf);
- opi->buffer_id = htonl(buffer_id);
- opi->total_len = htons(payload->size);
- opi->in_port = htons(in_port);
- opi->reason = reason;
- ofpbuf_put(buf, payload->data, send_len);
- update_openflow_length(buf);
-
- return buf;
-}
-
/* Creates and returns an OFPT_ECHO_REQUEST message with an empty payload. */
struct ofpbuf *
make_echo_request(void)
return b->size / ofputil_get_phy_port_size(ofp_version);
}
-static enum ofperr
-check_resubmit_table(const struct nx_action_resubmit *nar)
-{
- if (nar->pad[0] || nar->pad[1] || nar->pad[2]) {
- return OFPERR_OFPBAC_BAD_ARGUMENT;
- }
- return 0;
-}
-
-static enum ofperr
-check_output_reg(const struct nx_action_output_reg *naor,
- const struct flow *flow)
-{
- struct mf_subfield src;
- size_t i;
-
- for (i = 0; i < sizeof naor->zero; i++) {
- if (naor->zero[i]) {
- return OFPERR_OFPBAC_BAD_ARGUMENT;
- }
- }
-
- nxm_decode(&src, naor->src, naor->ofs_nbits);
- return mf_check_src(&src, flow);
-}
-
-enum ofperr
-validate_actions(const union ofp_action *actions, size_t n_actions,
- const struct flow *flow, int max_ports)
-{
- const union ofp_action *a;
- size_t left;
-
- OFPUTIL_ACTION_FOR_EACH (a, left, actions, n_actions) {
- enum ofperr error;
- uint16_t port;
- int code;
-
- code = ofputil_decode_action(a);
- if (code < 0) {
- error = -code;
- VLOG_WARN_RL(&bad_ofmsg_rl,
- "action decoding error at offset %td (%s)",
- (a - actions) * sizeof *a, ofperr_get_name(error));
-
- return error;
- }
-
- error = 0;
- switch ((enum ofputil_action_code) code) {
- case OFPUTIL_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.
ofputil_action_code_from_name(const char *name)
{
static const char *names[OFPUTIL_N_ACTIONS] = {
-#define OFPAT10_ACTION(ENUM, STRUCT, NAME) NAME,
+ NULL,
+#define OFPAT10_ACTION(ENUM, STRUCT, NAME) NAME,
+#define OFPAT11_ACTION(ENUM, STRUCT, NAME) NAME,
#define NXAST_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME) NAME,
#include "ofp-util.def"
};
ofputil_put_action(enum ofputil_action_code code, struct ofpbuf *buf)
{
switch (code) {
+ case OFPUTIL_ACTION_INVALID:
+ NOT_REACHED();
+
#define OFPAT10_ACTION(ENUM, STRUCT, NAME) \
case OFPUTIL_##ENUM: return ofputil_put_##ENUM(buf);
+#define OFPAT11_ACTION OFPAT10_ACTION
#define NXAST_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME) \
case OFPUTIL_##ENUM: return ofputil_put_##ENUM(buf);
#include "ofp-util.def"
ofputil_init_##ENUM(s); \
return s; \
}
+#define OFPAT11_ACTION OFPAT10_ACTION
#define NXAST_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME) \
void \
ofputil_init_##ENUM(struct STRUCT *s) \
}
#include "ofp-util.def"
-/* Returns true if 'action' outputs to 'port', false otherwise. */
-bool
-action_outputs_to_port(const union ofp_action *action, ovs_be16 port)
-{
- switch (ofputil_decode_action(action)) {
- case OFPUTIL_OFPAT10_OUTPUT:
- return action->output.port == port;
- case OFPUTIL_OFPAT10_ENQUEUE:
- return ((const struct ofp_action_enqueue *) action)->port == port;
- case OFPUTIL_NXAST_CONTROLLER:
- return port == htons(OFPP_CONTROLLER);
- default:
- return false;
- }
-}
-
/* "Normalizes" the wildcards in 'rule'. That means:
*
* 1. If the type of level N is known, then only the valid fields for that
}
}
-/* Attempts to pull 'actions_len' bytes from the front of 'b'. Returns 0 if
- * successful, otherwise an OpenFlow error.
- *
- * If successful, the first action is stored in '*actionsp' and the number of
- * "union ofp_action" size elements into '*n_actionsp'. Otherwise NULL and 0
- * are stored, respectively.
- *
- * This function does not check that the actions are valid (the caller should
- * do so, with validate_actions()). The caller is also responsible for making
- * sure that 'b->data' is initially aligned appropriately for "union
- * ofp_action". */
-enum ofperr
-ofputil_pull_actions(struct ofpbuf *b, unsigned int actions_len,
- union ofp_action **actionsp, size_t *n_actionsp)
-{
- if (actions_len % OFP_ACTION_ALIGN != 0) {
- VLOG_WARN_RL(&bad_ofmsg_rl, "OpenFlow message actions length %u "
- "is not a multiple of %d", actions_len, OFP_ACTION_ALIGN);
- goto error;
- }
-
- *actionsp = ofpbuf_try_pull(b, actions_len);
- if (*actionsp == NULL) {
- VLOG_WARN_RL(&bad_ofmsg_rl, "OpenFlow message actions length %u "
- "exceeds remaining message length (%zu)",
- actions_len, b->size);
- goto error;
- }
-
- *n_actionsp = actions_len / OFP_ACTION_ALIGN;
- return 0;
-
-error:
- *actionsp = NULL;
- *n_actionsp = 0;
- return OFPERR_OFPBRC_BAD_LEN;
-}
-
-bool
-ofputil_actions_equal(const union ofp_action *a, size_t n_a,
- const union ofp_action *b, size_t n_b)
-{
- return n_a == n_b && (!n_a || !memcmp(a, b, n_a * sizeof *a));
-}
-
-union ofp_action *
-ofputil_actions_clone(const union ofp_action *actions, size_t n)
-{
- return n ? xmemdup(actions, n * sizeof *actions) : NULL;
-}
-
/* Parses a key or a key-value pair from '*stringp'.
*
* On success: Stores the key into '*keyp'. Stores the value, if present, into