+ 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