void
ofputil_wildcard_from_ofpfw10(uint32_t ofpfw, struct flow_wildcards *wc)
{
- BUILD_ASSERT_DECL(FLOW_WC_SEQ == 12);
+ BUILD_ASSERT_DECL(FLOW_WC_SEQ == 14);
/* Initialize most of rule->wc. */
flow_wildcards_init_catchall(wc);
wc->wildcards = (OVS_FORCE flow_wildcards_t) ofpfw & WC_INVARIANTS;
/* Wildcard fields that aren't defined by ofp10_match or tun_id. */
- wc->wildcards |= (FWW_ARP_SHA | FWW_ARP_THA | FWW_NW_ECN | FWW_NW_TTL
- | FWW_IPV6_LABEL);
+ wc->wildcards |= FWW_NW_ECN | FWW_NW_TTL;
if (ofpfw & OFPFW10_NW_TOS) {
/* OpenFlow 1.0 defines a TOS wildcard, but it's much later in
} else if (rule->wc.vlan_tci_mask & htons(VLAN_CFI)
&& !(rule->flow.vlan_tci & htons(VLAN_CFI))) {
match->dl_vlan = htons(OFP10_VLAN_NONE);
+ ofpfw |= OFPFW10_DL_VLAN_PCP;
} else {
if (!(rule->wc.vlan_tci_mask & htons(VLAN_VID_MASK))) {
ofpfw |= OFPFW10_DL_VLAN;
{ OFPUTIL_NXT_SET_CONTROLLER_ID, OFP10_VERSION,
NXT_SET_CONTROLLER_ID, "NXT_SET_CONTROLLER_ID",
sizeof(struct nx_controller_id), 0 },
+
+ { OFPUTIL_NXT_FLOW_MONITOR_CANCEL, OFP10_VERSION,
+ NXT_FLOW_MONITOR_CANCEL, "NXT_FLOW_MONITOR_CANCEL",
+ sizeof(struct nx_flow_monitor_cancel), 0 },
+
+ { OFPUTIL_NXT_FLOW_MONITOR_PAUSED, OFP10_VERSION,
+ NXT_FLOW_MONITOR_PAUSED, "NXT_FLOW_MONITOR_PAUSED",
+ sizeof(struct nicira_header), 0 },
+
+ { OFPUTIL_NXT_FLOW_MONITOR_RESUMED, OFP10_VERSION,
+ NXT_FLOW_MONITOR_RESUMED, "NXT_FLOW_MONITOR_RESUMED",
+ sizeof(struct nicira_header), 0 },
};
static const struct ofputil_msg_category nxt_category = {
{ OFPUTIL_NXST_AGGREGATE_REQUEST, OFP10_VERSION,
NXST_AGGREGATE, "NXST_AGGREGATE request",
sizeof(struct nx_aggregate_stats_request), 8 },
+
+ { OFPUTIL_NXST_FLOW_MONITOR_REQUEST, OFP10_VERSION,
+ NXST_FLOW_MONITOR, "NXST_FLOW_MONITOR request",
+ sizeof(struct nicira_stats_msg), 8 },
};
static const struct ofputil_msg_category nxst_request_category = {
{ OFPUTIL_NXST_AGGREGATE_REPLY, OFP10_VERSION,
NXST_AGGREGATE, "NXST_AGGREGATE reply",
sizeof(struct nx_aggregate_stats_reply), 0 },
+
+ { OFPUTIL_NXST_FLOW_MONITOR_REPLY, OFP10_VERSION,
+ NXST_FLOW_MONITOR, "NXST_FLOW_MONITOR reply",
+ sizeof(struct nicira_stats_msg), 8 },
};
static const struct ofputil_msg_category nxst_reply_category = {
{
const struct flow_wildcards *wc = &rule->wc;
- BUILD_ASSERT_DECL(FLOW_WC_SEQ == 12);
+ BUILD_ASSERT_DECL(FLOW_WC_SEQ == 14);
/* NXM and OF1.1+ supports bitwise matching on ethernet addresses. */
if (!eth_mask_is_exact(wc->dl_src_mask)
}
/* Only NXM supports matching ARP hardware addresses. */
- if (!(wc->wildcards & FWW_ARP_SHA) || !(wc->wildcards & FWW_ARP_THA)) {
+ if (!eth_addr_is_zero(wc->arp_sha_mask) ||
+ !eth_addr_is_zero(wc->arp_tha_mask)) {
return OFPUTIL_P_NXM_ANY;
}
}
/* Only NXM supports matching IPv6 flow label. */
- if (!(wc->wildcards & FWW_IPV6_LABEL)) {
+ if (wc->ipv6_label_mask) {
return OFPUTIL_P_NXM_ANY;
}
if (osm->type == htons(OFPST_FLOW)) {
struct ofp_flow_stats *ofs;
- ofs = ofpbuf_put_uninit(reply, sizeof *ofs);
+ 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)));
- 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);
} else if (osm->type == htons(OFPST_VENDOR)) {
struct nx_flow_stats *nfs;
+ int match_len;
- nfs = ofpbuf_put_uninit(reply, sizeof *nfs);
+ 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_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(reply, 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);
- ofpacts_put_openflow10(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();
}
return b;
}
+\f
+/* ofputil_flow_monitor_request */
+
+/* Converts an NXST_FLOW_MONITOR request in 'msg' into an abstract
+ * ofputil_flow_monitor_request in 'rq'.
+ *
+ * Multiple NXST_FLOW_MONITOR requests can be packed into a single OpenFlow
+ * message. Calling this function multiple times for a single 'msg' iterates
+ * through the requests. The caller must initially leave 'msg''s layer
+ * pointers null and not modify them between calls.
+ *
+ * Returns 0 if successful, EOF if no requests were left in this 'msg',
+ * otherwise an OFPERR_* value. */
+int
+ofputil_decode_flow_monitor_request(struct ofputil_flow_monitor_request *rq,
+ struct ofpbuf *msg)
+{
+ struct nx_flow_monitor_request *nfmr;
+ uint16_t flags;
+
+ if (!msg->l2) {
+ msg->l2 = msg->data;
+ ofpbuf_pull(msg, sizeof(struct nicira_stats_msg));
+ }
+
+ if (!msg->size) {
+ return EOF;
+ }
+
+ nfmr = ofpbuf_try_pull(msg, sizeof *nfmr);
+ if (!nfmr) {
+ VLOG_WARN_RL(&bad_ofmsg_rl, "NXST_FLOW_MONITOR request has %zu "
+ "leftover bytes at end", msg->size);
+ return OFPERR_OFPBRC_BAD_LEN;
+ }
+
+ flags = ntohs(nfmr->flags);
+ if (!(flags & (NXFMF_ADD | NXFMF_DELETE | NXFMF_MODIFY))
+ || flags & ~(NXFMF_INITIAL | NXFMF_ADD | NXFMF_DELETE
+ | NXFMF_MODIFY | NXFMF_ACTIONS | NXFMF_OWN)) {
+ VLOG_WARN_RL(&bad_ofmsg_rl, "NXST_FLOW_MONITOR has bad flags %#"PRIx16,
+ flags);
+ return OFPERR_NXBRC_FM_BAD_FLAGS;
+ }
+
+ if (!is_all_zeros(nfmr->zeros, sizeof nfmr->zeros)) {
+ return OFPERR_NXBRC_MUST_BE_ZERO;
+ }
+
+ rq->id = ntohl(nfmr->id);
+ rq->flags = flags;
+ rq->out_port = ntohs(nfmr->out_port);
+ rq->table_id = nfmr->table_id;
+
+ return nx_pull_match(msg, ntohs(nfmr->match_len), OFP_DEFAULT_PRIORITY,
+ &rq->match, NULL, NULL);
+}
+
+void
+ofputil_append_flow_monitor_request(
+ const struct ofputil_flow_monitor_request *rq, struct ofpbuf *msg)
+{
+ struct nx_flow_monitor_request *nfmr;
+ size_t start_ofs;
+ int match_len;
+
+ if (!msg->size) {
+ ofputil_put_stats_header(alloc_xid(), OFPT10_STATS_REQUEST,
+ htons(OFPST_VENDOR),
+ htonl(NXST_FLOW_MONITOR), msg);
+ }
+
+ start_ofs = msg->size;
+ ofpbuf_put_zeros(msg, sizeof *nfmr);
+ match_len = nx_put_match(msg, false, &rq->match, htonll(0), htonll(0));
+
+ nfmr = ofpbuf_at_assert(msg, start_ofs, sizeof *nfmr);
+ nfmr->id = htonl(rq->id);
+ nfmr->flags = htons(rq->flags);
+ nfmr->out_port = htons(rq->out_port);
+ nfmr->match_len = htons(match_len);
+ nfmr->table_id = rq->table_id;
+}
+
+/* Converts an NXST_FLOW_MONITOR reply (also known as a flow update) in 'msg'
+ * into an abstract ofputil_flow_update in 'update'. The caller must have
+ * initialized update->match to point to space allocated for a cls_rule.
+ *
+ * Uses 'ofpacts' to store the abstract OFPACT_* version of the update's
+ * actions (except for NXFME_ABBREV, which never includes actions). The caller
+ * must initialize 'ofpacts' and retains ownership of it. 'update->ofpacts'
+ * will point into the 'ofpacts' buffer.
+ *
+ * Multiple flow updates can be packed into a single OpenFlow message. Calling
+ * this function multiple times for a single 'msg' iterates through the
+ * updates. The caller must initially leave 'msg''s layer pointers null and
+ * not modify them between calls.
+ *
+ * Returns 0 if successful, EOF if no updates were left in this 'msg',
+ * otherwise an OFPERR_* value. */
+int
+ofputil_decode_flow_update(struct ofputil_flow_update *update,
+ struct ofpbuf *msg, struct ofpbuf *ofpacts)
+{
+ struct nx_flow_update_header *nfuh;
+ unsigned int length;
+
+ if (!msg->l2) {
+ msg->l2 = msg->data;
+ ofpbuf_pull(msg, sizeof(struct nicira_stats_msg));
+ }
+
+ if (!msg->size) {
+ return EOF;
+ }
+
+ if (msg->size < sizeof(struct nx_flow_update_header)) {
+ goto bad_len;
+ }
+
+ nfuh = msg->data;
+ update->event = ntohs(nfuh->event);
+ length = ntohs(nfuh->length);
+ if (length > msg->size || length % 8) {
+ goto bad_len;
+ }
+
+ if (update->event == NXFME_ABBREV) {
+ struct nx_flow_update_abbrev *nfua;
+
+ if (length != sizeof *nfua) {
+ goto bad_len;
+ }
+
+ nfua = ofpbuf_pull(msg, sizeof *nfua);
+ update->xid = nfua->xid;
+ return 0;
+ } else if (update->event == NXFME_ADDED
+ || update->event == NXFME_DELETED
+ || update->event == NXFME_MODIFIED) {
+ struct nx_flow_update_full *nfuf;
+ unsigned int actions_len;
+ unsigned int match_len;
+ enum ofperr error;
+
+ if (length < sizeof *nfuf) {
+ goto bad_len;
+ }
+
+ nfuf = ofpbuf_pull(msg, sizeof *nfuf);
+ match_len = ntohs(nfuf->match_len);
+ if (sizeof *nfuf + match_len > length) {
+ goto bad_len;
+ }
+
+ update->reason = ntohs(nfuf->reason);
+ update->idle_timeout = ntohs(nfuf->idle_timeout);
+ update->hard_timeout = ntohs(nfuf->hard_timeout);
+ update->table_id = nfuf->table_id;
+ update->cookie = nfuf->cookie;
+
+ error = nx_pull_match(msg, match_len, ntohs(nfuf->priority),
+ update->match, NULL, NULL);
+ if (error) {
+ return error;
+ }
+
+ actions_len = length - sizeof *nfuf - ROUND_UP(match_len, 8);
+ error = ofpacts_pull_openflow10(msg, actions_len, ofpacts);
+ if (error) {
+ return error;
+ }
+
+ update->ofpacts = ofpacts->data;
+ update->ofpacts_len = ofpacts->size;
+ return 0;
+ } else {
+ VLOG_WARN_RL(&bad_ofmsg_rl,
+ "NXST_FLOW_MONITOR reply has bad event %"PRIu16,
+ ntohs(nfuh->event));
+ return OFPERR_OFPET_BAD_REQUEST;
+ }
+
+bad_len:
+ VLOG_WARN_RL(&bad_ofmsg_rl, "NXST_FLOW_MONITOR reply has %zu "
+ "leftover bytes at end", msg->size);
+ return OFPERR_OFPBRC_BAD_LEN;
+}
+
+uint32_t
+ofputil_decode_flow_monitor_cancel(const struct ofp_header *oh)
+{
+ return ntohl(((const struct nx_flow_monitor_cancel *) oh)->id);
+}
+
+struct ofpbuf *
+ofputil_encode_flow_monitor_cancel(uint32_t id)
+{
+ struct nx_flow_monitor_cancel *nfmc;
+ struct ofpbuf *msg;
+
+ nfmc = make_nxmsg(sizeof *nfmc, NXT_FLOW_MONITOR_CANCEL, &msg);
+ nfmc->id = htonl(id);
+ return msg;
+}
+
+void
+ofputil_start_flow_update(struct list *replies)
+{
+ struct ofpbuf *msg;
+
+ msg = ofpbuf_new(1024);
+ ofputil_put_stats_header(htonl(0), OFPT10_STATS_REPLY,
+ htons(OFPST_VENDOR),
+ htonl(NXST_FLOW_MONITOR), msg);
+
+ list_init(replies);
+ list_push_back(replies, &msg->list_node);
+}
+
+void
+ofputil_append_flow_update(const struct ofputil_flow_update *update,
+ struct list *replies)
+{
+ struct nx_flow_update_header *nfuh;
+ struct ofpbuf *msg;
+ size_t start_ofs;
+
+ msg = ofpbuf_from_list(list_back(replies));
+ start_ofs = msg->size;
+
+ if (update->event == NXFME_ABBREV) {
+ struct nx_flow_update_abbrev *nfua;
+
+ nfua = ofpbuf_put_zeros(msg, sizeof *nfua);
+ nfua->xid = update->xid;
+ } else {
+ struct nx_flow_update_full *nfuf;
+ int match_len;
+
+ ofpbuf_put_zeros(msg, sizeof *nfuf);
+ match_len = nx_put_match(msg, false, update->match,
+ htonll(0), htonll(0));
+ ofpacts_put_openflow10(update->ofpacts, update->ofpacts_len, msg);
+
+ nfuf = ofpbuf_at_assert(msg, start_ofs, sizeof *nfuf);
+ nfuf->reason = htons(update->reason);
+ nfuf->priority = htons(update->match->priority);
+ nfuf->idle_timeout = htons(update->idle_timeout);
+ nfuf->hard_timeout = htons(update->hard_timeout);
+ nfuf->match_len = htons(match_len);
+ nfuf->table_id = update->table_id;
+ nfuf->cookie = update->cookie;
+ }
+
+ nfuh = ofpbuf_at_assert(msg, start_ofs, sizeof *nfuh);
+ nfuh->length = htons(msg->size - start_ofs);
+ nfuh->event = htons(update->event);
+ ofputil_postappend_stats_reply(start_ofs, replies);
+}
+\f
struct ofpbuf *
ofputil_encode_packet_out(const struct ofputil_packet_out *po)
{
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'
wc.wildcards |= FWW_NW_TTL;
}
if (!(may_match & MAY_ARP_SHA)) {
- wc.wildcards |= FWW_ARP_SHA;
+ memset(wc.arp_sha_mask, 0, ETH_ADDR_LEN);
}
if (!(may_match & MAY_ARP_THA)) {
- wc.wildcards |= FWW_ARP_THA;
+ memset(wc.arp_tha_mask, 0, ETH_ADDR_LEN);
}
if (!(may_match & MAY_IPV6)) {
wc.ipv6_src_mask = wc.ipv6_dst_mask = in6addr_any;
- wc.wildcards |= FWW_IPV6_LABEL;
+ wc.ipv6_label_mask = htonl(0);
}
if (!(may_match & MAY_ND_TARGET)) {
wc.nd_target_mask = in6addr_any;