+/* Converts abstract flow_stats_request 'fsr' into an OFPST_FLOW,
+ * OFPST_AGGREGATE, NXST_FLOW, or NXST_AGGREGATE request 'oh' according to
+ * 'protocol', and returns the message. */
+struct ofpbuf *
+ofputil_encode_flow_stats_request(const struct ofputil_flow_stats_request *fsr,
+ enum ofputil_protocol protocol)
+{
+ struct ofpbuf *msg;
+ enum ofpraw raw;
+
+ switch (protocol) {
+ case OFPUTIL_P_OF11_STD:
+ case OFPUTIL_P_OF12_OXM:
+ case OFPUTIL_P_OF13_OXM: {
+ struct ofp11_flow_stats_request *ofsr;
+
+ raw = (fsr->aggregate
+ ? OFPRAW_OFPST11_AGGREGATE_REQUEST
+ : OFPRAW_OFPST11_FLOW_REQUEST);
+ msg = ofpraw_alloc(raw, ofputil_protocol_to_ofp_version(protocol),
+ ofputil_match_typical_len(protocol));
+ ofsr = ofpbuf_put_zeros(msg, sizeof *ofsr);
+ ofsr->table_id = fsr->table_id;
+ ofsr->out_port = ofputil_port_to_ofp11(fsr->out_port);
+ ofsr->out_group = htonl(fsr->out_group);
+ ofsr->cookie = fsr->cookie;
+ ofsr->cookie_mask = fsr->cookie_mask;
+ ofputil_put_ofp11_match(msg, &fsr->match, protocol);
+ break;
+ }
+
+ case OFPUTIL_P_OF10_STD:
+ case OFPUTIL_P_OF10_STD_TID: {
+ struct ofp10_flow_stats_request *ofsr;
+
+ raw = (fsr->aggregate
+ ? OFPRAW_OFPST10_AGGREGATE_REQUEST
+ : OFPRAW_OFPST10_FLOW_REQUEST);
+ msg = ofpraw_alloc(raw, OFP10_VERSION, 0);
+ ofsr = ofpbuf_put_zeros(msg, sizeof *ofsr);
+ ofputil_match_to_ofp10_match(&fsr->match, &ofsr->match);
+ ofsr->table_id = fsr->table_id;
+ ofsr->out_port = htons(ofp_to_u16(fsr->out_port));
+ break;
+ }
+
+ case OFPUTIL_P_OF10_NXM:
+ case OFPUTIL_P_OF10_NXM_TID: {
+ struct nx_flow_stats_request *nfsr;
+ int match_len;
+
+ raw = (fsr->aggregate
+ ? OFPRAW_NXST_AGGREGATE_REQUEST
+ : OFPRAW_NXST_FLOW_REQUEST);
+ msg = ofpraw_alloc(raw, OFP10_VERSION, NXM_TYPICAL_LEN);
+ ofpbuf_put_zeros(msg, sizeof *nfsr);
+ match_len = nx_put_match(msg, &fsr->match,
+ fsr->cookie, fsr->cookie_mask);
+
+ nfsr = msg->l3;
+ nfsr->out_port = htons(ofp_to_u16(fsr->out_port));
+ nfsr->match_len = htons(match_len);
+ nfsr->table_id = fsr->table_id;
+ break;
+ }
+
+ default:
+ NOT_REACHED();
+ }
+
+ return msg;
+}
+
+/* Converts an OFPST_FLOW or NXST_FLOW reply in 'msg' into an abstract
+ * ofputil_flow_stats in 'fs'.
+ *
+ * Multiple OFPST_FLOW or NXST_FLOW replies can be packed into a single
+ * OpenFlow message. Calling this function multiple times for a single 'msg'
+ * iterates through the replies. The caller must initially leave 'msg''s layer
+ * pointers null and not modify them between calls.
+ *
+ * Most switches don't send the values needed to populate fs->idle_age and
+ * fs->hard_age, so those members will usually be set to 0. If the switch from
+ * which 'msg' originated is known to implement NXT_FLOW_AGE, then pass
+ * '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,
+ struct ofpbuf *ofpacts)
+{
+ const struct ofp_header *oh;
+ enum ofperr error;
+ enum ofpraw raw;
+
+ error = (msg->l2
+ ? ofpraw_decode(&raw, msg->l2)
+ : ofpraw_pull(&raw, msg));
+ if (error) {
+ return error;
+ }
+ oh = msg->l2;
+
+ if (!msg->size) {
+ return EOF;
+ } else if (raw == OFPRAW_OFPST11_FLOW_REPLY
+ || raw == OFPRAW_OFPST13_FLOW_REPLY) {
+ const struct ofp11_flow_stats *ofs;
+ size_t length;
+ uint16_t padded_match_len;
+
+ ofs = ofpbuf_try_pull(msg, sizeof *ofs);
+ if (!ofs) {
+ VLOG_WARN_RL(&bad_ofmsg_rl, "OFPST_FLOW reply has %zu leftover "
+ "bytes at end", msg->size);
+ return EINVAL;
+ }
+
+ length = ntohs(ofs->length);
+ if (length < sizeof *ofs) {
+ VLOG_WARN_RL(&bad_ofmsg_rl, "OFPST_FLOW reply claims invalid "
+ "length %zu", length);
+ return EINVAL;
+ }
+
+ if (ofputil_pull_ofp11_match(msg, &fs->match, &padded_match_len)) {
+ VLOG_WARN_RL(&bad_ofmsg_rl, "OFPST_FLOW reply bad match");
+ return EINVAL;
+ }
+
+ if (ofpacts_pull_openflow_instructions(msg, length - sizeof *ofs -
+ padded_match_len, oh->version,
+ ofpacts)) {
+ VLOG_WARN_RL(&bad_ofmsg_rl, "OFPST_FLOW reply bad instructions");
+ return EINVAL;
+ }
+
+ fs->priority = ntohs(ofs->priority);
+ fs->table_id = ofs->table_id;
+ fs->duration_sec = ntohl(ofs->duration_sec);
+ fs->duration_nsec = ntohl(ofs->duration_nsec);
+ fs->idle_timeout = ntohs(ofs->idle_timeout);
+ fs->hard_timeout = ntohs(ofs->hard_timeout);
+ if (raw == OFPRAW_OFPST13_FLOW_REPLY) {
+ error = ofputil_decode_flow_mod_flags(ofs->flags, -1, oh->version,
+ &fs->flags);
+ if (error) {
+ return error;
+ }
+ } else {
+ fs->flags = 0;
+ }
+ fs->idle_age = -1;
+ fs->hard_age = -1;
+ fs->cookie = ofs->cookie;
+ fs->packet_count = ntohll(ofs->packet_count);
+ fs->byte_count = ntohll(ofs->byte_count);
+ } else if (raw == OFPRAW_OFPST10_FLOW_REPLY) {
+ const struct ofp10_flow_stats *ofs;
+ size_t length;
+
+ ofs = ofpbuf_try_pull(msg, sizeof *ofs);
+ if (!ofs) {
+ VLOG_WARN_RL(&bad_ofmsg_rl, "OFPST_FLOW reply has %zu leftover "
+ "bytes at end", msg->size);
+ return EINVAL;
+ }
+
+ length = ntohs(ofs->length);
+ if (length < sizeof *ofs) {
+ VLOG_WARN_RL(&bad_ofmsg_rl, "OFPST_FLOW reply claims invalid "
+ "length %zu", length);
+ return EINVAL;
+ }
+
+ if (ofpacts_pull_openflow_actions(msg, length - sizeof *ofs,
+ oh->version, ofpacts)) {
+ return EINVAL;
+ }
+
+ fs->cookie = get_32aligned_be64(&ofs->cookie);
+ ofputil_match_from_ofp10_match(&ofs->match, &fs->match);
+ fs->priority = ntohs(ofs->priority);
+ fs->table_id = ofs->table_id;
+ fs->duration_sec = ntohl(ofs->duration_sec);
+ fs->duration_nsec = ntohl(ofs->duration_nsec);
+ fs->idle_timeout = ntohs(ofs->idle_timeout);
+ fs->hard_timeout = ntohs(ofs->hard_timeout);
+ fs->idle_age = -1;
+ fs->hard_age = -1;
+ fs->packet_count = ntohll(get_32aligned_be64(&ofs->packet_count));
+ fs->byte_count = ntohll(get_32aligned_be64(&ofs->byte_count));
+ fs->flags = 0;
+ } else if (raw == OFPRAW_NXST_FLOW_REPLY) {
+ const struct nx_flow_stats *nfs;
+ size_t match_len, actions_len, length;
+
+ nfs = ofpbuf_try_pull(msg, sizeof *nfs);
+ if (!nfs) {
+ VLOG_WARN_RL(&bad_ofmsg_rl, "NXST_FLOW reply has %zu leftover "
+ "bytes at end", msg->size);
+ return EINVAL;
+ }
+
+ length = ntohs(nfs->length);
+ match_len = ntohs(nfs->match_len);
+ if (length < sizeof *nfs + ROUND_UP(match_len, 8)) {
+ VLOG_WARN_RL(&bad_ofmsg_rl, "NXST_FLOW reply with match_len=%zu "
+ "claims invalid length %zu", match_len, length);
+ return EINVAL;
+ }
+ if (nx_pull_match(msg, match_len, &fs->match, NULL, NULL)) {
+ return EINVAL;
+ }
+
+ actions_len = length - sizeof *nfs - ROUND_UP(match_len, 8);
+ if (ofpacts_pull_openflow_actions(msg, actions_len, oh->version,
+ ofpacts)) {
+ return EINVAL;
+ }
+
+ fs->cookie = nfs->cookie;
+ fs->table_id = nfs->table_id;
+ fs->duration_sec = ntohl(nfs->duration_sec);
+ fs->duration_nsec = ntohl(nfs->duration_nsec);
+ fs->priority = ntohs(nfs->priority);
+ fs->idle_timeout = ntohs(nfs->idle_timeout);
+ fs->hard_timeout = ntohs(nfs->hard_timeout);
+ fs->idle_age = -1;
+ fs->hard_age = -1;
+ if (flow_age_extension) {
+ if (nfs->idle_age) {
+ fs->idle_age = ntohs(nfs->idle_age) - 1;
+ }
+ if (nfs->hard_age) {
+ fs->hard_age = ntohs(nfs->hard_age) - 1;
+ }
+ }
+ fs->packet_count = ntohll(nfs->packet_count);
+ fs->byte_count = ntohll(nfs->byte_count);
+ fs->flags = 0;
+ } else {
+ NOT_REACHED();
+ }
+
+ fs->ofpacts = ofpacts->data;
+ fs->ofpacts_len = ofpacts->size;
+
+ return 0;
+}
+
+/* Returns 'count' unchanged except that UINT64_MAX becomes 0.
+ *
+ * We use this in situations where OVS internally uses UINT64_MAX to mean
+ * "value unknown" but OpenFlow 1.0 does not define any unknown value. */
+static uint64_t
+unknown_to_zero(uint64_t count)
+{
+ return count != UINT64_MAX ? count : 0;
+}
+
+/* Appends an OFPST_FLOW or NXST_FLOW reply that contains the data in 'fs' to
+ * those already present in the list of ofpbufs in 'replies'. 'replies' should
+ * have been initialized with ofpmp_init(). */
+void
+ofputil_append_flow_stats_reply(const struct ofputil_flow_stats *fs,
+ struct list *replies)
+{
+ struct ofpbuf *reply = ofpbuf_from_list(list_back(replies));
+ size_t start_ofs = reply->size;
+ enum ofpraw raw;
+ enum ofp_version version = ((struct ofp_header *)reply->data)->version;
+
+ ofpraw_decode_partial(&raw, reply->data, reply->size);
+ if (raw == OFPRAW_OFPST11_FLOW_REPLY || raw == OFPRAW_OFPST13_FLOW_REPLY) {
+ struct ofp11_flow_stats *ofs;
+
+ ofpbuf_put_uninit(reply, sizeof *ofs);
+ oxm_put_match(reply, &fs->match);
+ ofpacts_put_openflow_instructions(fs->ofpacts, fs->ofpacts_len, reply,
+ version);
+
+ 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;
+ ofs->duration_sec = htonl(fs->duration_sec);
+ ofs->duration_nsec = htonl(fs->duration_nsec);
+ ofs->priority = htons(fs->priority);
+ ofs->idle_timeout = htons(fs->idle_timeout);
+ ofs->hard_timeout = htons(fs->hard_timeout);
+ if (raw == OFPRAW_OFPST13_FLOW_REPLY) {
+ ofs->flags = ofputil_encode_flow_mod_flags(fs->flags, version);
+ } else {
+ ofs->flags = 0;
+ }
+ memset(ofs->pad2, 0, sizeof ofs->pad2);
+ ofs->cookie = fs->cookie;
+ ofs->packet_count = htonll(unknown_to_zero(fs->packet_count));
+ ofs->byte_count = htonll(unknown_to_zero(fs->byte_count));
+ } else if (raw == OFPRAW_OFPST10_FLOW_REPLY) {
+ struct ofp10_flow_stats *ofs;
+
+ ofpbuf_put_uninit(reply, sizeof *ofs);
+ ofpacts_put_openflow_actions(fs->ofpacts, fs->ofpacts_len, reply,
+ version);
+ 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_match_to_ofp10_match(&fs->match, &ofs->match);
+ ofs->duration_sec = htonl(fs->duration_sec);
+ ofs->duration_nsec = htonl(fs->duration_nsec);
+ ofs->priority = htons(fs->priority);
+ ofs->idle_timeout = htons(fs->idle_timeout);
+ ofs->hard_timeout = htons(fs->hard_timeout);
+ memset(ofs->pad2, 0, sizeof ofs->pad2);
+ put_32aligned_be64(&ofs->cookie, fs->cookie);
+ put_32aligned_be64(&ofs->packet_count,
+ htonll(unknown_to_zero(fs->packet_count)));
+ put_32aligned_be64(&ofs->byte_count,
+ htonll(unknown_to_zero(fs->byte_count)));
+ } else if (raw == OFPRAW_NXST_FLOW_REPLY) {
+ struct nx_flow_stats *nfs;
+ int match_len;
+
+ ofpbuf_put_uninit(reply, sizeof *nfs);
+ match_len = nx_put_match(reply, &fs->match, 0, 0);
+ ofpacts_put_openflow_actions(fs->ofpacts, fs->ofpacts_len, reply,
+ version);
+ 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->duration_nsec = htonl(fs->duration_nsec);
+ nfs->priority = htons(fs->priority);
+ nfs->idle_timeout = htons(fs->idle_timeout);
+ nfs->hard_timeout = htons(fs->hard_timeout);
+ nfs->idle_age = htons(fs->idle_age < 0 ? 0
+ : fs->idle_age < UINT16_MAX ? fs->idle_age + 1
+ : UINT16_MAX);
+ nfs->hard_age = htons(fs->hard_age < 0 ? 0
+ : fs->hard_age < UINT16_MAX ? fs->hard_age + 1
+ : UINT16_MAX);
+ nfs->match_len = htons(match_len);
+ nfs->cookie = fs->cookie;
+ nfs->packet_count = htonll(fs->packet_count);
+ nfs->byte_count = htonll(fs->byte_count);
+ } else {
+ NOT_REACHED();
+ }
+
+ ofpmp_postappend(replies, start_ofs);
+}
+
+/* Converts abstract ofputil_aggregate_stats 'stats' into an OFPST_AGGREGATE or
+ * NXST_AGGREGATE reply matching 'request', and returns the message. */
+struct ofpbuf *
+ofputil_encode_aggregate_stats_reply(
+ const struct ofputil_aggregate_stats *stats,
+ const struct ofp_header *request)
+{
+ struct ofp_aggregate_stats_reply *asr;
+ uint64_t packet_count;
+ uint64_t byte_count;
+ struct ofpbuf *msg;
+ enum ofpraw raw;
+
+ ofpraw_decode(&raw, request);
+ if (raw == OFPRAW_OFPST10_AGGREGATE_REQUEST) {
+ packet_count = unknown_to_zero(stats->packet_count);
+ byte_count = unknown_to_zero(stats->byte_count);
+ } else {
+ packet_count = stats->packet_count;
+ byte_count = stats->byte_count;
+ }
+
+ msg = ofpraw_alloc_stats_reply(request, 0);
+ asr = ofpbuf_put_zeros(msg, sizeof *asr);
+ put_32aligned_be64(&asr->packet_count, htonll(packet_count));
+ put_32aligned_be64(&asr->byte_count, htonll(byte_count));
+ asr->flow_count = htonl(stats->flow_count);
+
+ return msg;
+}
+
+enum ofperr
+ofputil_decode_aggregate_stats_reply(struct ofputil_aggregate_stats *stats,
+ const struct ofp_header *reply)
+{
+ struct ofp_aggregate_stats_reply *asr;
+ struct ofpbuf msg;
+
+ ofpbuf_use_const(&msg, reply, ntohs(reply->length));
+ ofpraw_pull_assert(&msg);
+
+ asr = msg.l3;
+ stats->packet_count = ntohll(get_32aligned_be64(&asr->packet_count));
+ stats->byte_count = ntohll(get_32aligned_be64(&asr->byte_count));
+ stats->flow_count = ntohl(asr->flow_count);
+
+ return 0;
+}
+
+/* Converts an OFPT_FLOW_REMOVED or NXT_FLOW_REMOVED message 'oh' into an
+ * abstract ofputil_flow_removed in 'fr'. Returns 0 if successful, otherwise
+ * an OpenFlow error code. */
+enum ofperr
+ofputil_decode_flow_removed(struct ofputil_flow_removed *fr,
+ const struct ofp_header *oh)
+{
+ enum ofpraw raw;
+ struct ofpbuf b;
+
+ ofpbuf_use_const(&b, oh, ntohs(oh->length));
+ raw = ofpraw_pull_assert(&b);
+ if (raw == OFPRAW_OFPT11_FLOW_REMOVED) {
+ const struct ofp12_flow_removed *ofr;
+ enum ofperr error;
+
+ ofr = ofpbuf_pull(&b, sizeof *ofr);
+
+ error = ofputil_pull_ofp11_match(&b, &fr->match, NULL);
+ if (error) {
+ return error;
+ }
+
+ fr->priority = ntohs(ofr->priority);
+ fr->cookie = ofr->cookie;
+ fr->reason = ofr->reason;
+ fr->table_id = ofr->table_id;
+ fr->duration_sec = ntohl(ofr->duration_sec);
+ fr->duration_nsec = ntohl(ofr->duration_nsec);
+ fr->idle_timeout = ntohs(ofr->idle_timeout);
+ fr->hard_timeout = ntohs(ofr->hard_timeout);
+ fr->packet_count = ntohll(ofr->packet_count);
+ fr->byte_count = ntohll(ofr->byte_count);
+ } else if (raw == OFPRAW_OFPT10_FLOW_REMOVED) {
+ const struct ofp10_flow_removed *ofr;
+
+ ofr = ofpbuf_pull(&b, sizeof *ofr);
+
+ ofputil_match_from_ofp10_match(&ofr->match, &fr->match);
+ fr->priority = ntohs(ofr->priority);
+ fr->cookie = ofr->cookie;
+ fr->reason = ofr->reason;
+ fr->table_id = 255;
+ fr->duration_sec = ntohl(ofr->duration_sec);
+ fr->duration_nsec = ntohl(ofr->duration_nsec);
+ fr->idle_timeout = ntohs(ofr->idle_timeout);
+ fr->hard_timeout = 0;
+ fr->packet_count = ntohll(ofr->packet_count);
+ fr->byte_count = ntohll(ofr->byte_count);
+ } else if (raw == OFPRAW_NXT_FLOW_REMOVED) {
+ struct nx_flow_removed *nfr;
+ enum ofperr error;
+
+ nfr = ofpbuf_pull(&b, sizeof *nfr);
+ error = nx_pull_match(&b, ntohs(nfr->match_len), &fr->match,
+ NULL, NULL);
+ if (error) {
+ return error;
+ }
+ if (b.size) {
+ return OFPERR_OFPBRC_BAD_LEN;
+ }
+
+ fr->priority = ntohs(nfr->priority);
+ fr->cookie = nfr->cookie;
+ fr->reason = nfr->reason;
+ fr->table_id = nfr->table_id ? nfr->table_id - 1 : 255;
+ fr->duration_sec = ntohl(nfr->duration_sec);
+ fr->duration_nsec = ntohl(nfr->duration_nsec);
+ fr->idle_timeout = ntohs(nfr->idle_timeout);
+ fr->hard_timeout = 0;
+ fr->packet_count = ntohll(nfr->packet_count);
+ fr->byte_count = ntohll(nfr->byte_count);
+ } else {
+ NOT_REACHED();
+ }
+
+ return 0;
+}
+
+/* Converts abstract ofputil_flow_removed 'fr' into an OFPT_FLOW_REMOVED or
+ * NXT_FLOW_REMOVED message 'oh' according to 'protocol', and returns the
+ * message. */
+struct ofpbuf *
+ofputil_encode_flow_removed(const struct ofputil_flow_removed *fr,
+ enum ofputil_protocol protocol)
+{
+ struct ofpbuf *msg;
+
+ switch (protocol) {
+ case OFPUTIL_P_OF11_STD:
+ case OFPUTIL_P_OF12_OXM:
+ case OFPUTIL_P_OF13_OXM: {
+ struct ofp12_flow_removed *ofr;
+
+ msg = ofpraw_alloc_xid(OFPRAW_OFPT11_FLOW_REMOVED,
+ ofputil_protocol_to_ofp_version(protocol),
+ htonl(0),
+ ofputil_match_typical_len(protocol));
+ ofr = ofpbuf_put_zeros(msg, sizeof *ofr);
+ ofr->cookie = fr->cookie;
+ ofr->priority = htons(fr->priority);
+ ofr->reason = fr->reason;
+ ofr->table_id = fr->table_id;
+ ofr->duration_sec = htonl(fr->duration_sec);
+ ofr->duration_nsec = htonl(fr->duration_nsec);
+ ofr->idle_timeout = htons(fr->idle_timeout);
+ ofr->hard_timeout = htons(fr->hard_timeout);
+ ofr->packet_count = htonll(fr->packet_count);
+ ofr->byte_count = htonll(fr->byte_count);
+ ofputil_put_ofp11_match(msg, &fr->match, protocol);
+ break;
+ }
+
+ case OFPUTIL_P_OF10_STD:
+ case OFPUTIL_P_OF10_STD_TID: {
+ struct ofp10_flow_removed *ofr;
+
+ msg = ofpraw_alloc_xid(OFPRAW_OFPT10_FLOW_REMOVED, OFP10_VERSION,
+ htonl(0), 0);
+ ofr = ofpbuf_put_zeros(msg, sizeof *ofr);
+ ofputil_match_to_ofp10_match(&fr->match, &ofr->match);
+ ofr->cookie = fr->cookie;
+ ofr->priority = htons(fr->priority);
+ ofr->reason = fr->reason;
+ ofr->duration_sec = htonl(fr->duration_sec);
+ ofr->duration_nsec = htonl(fr->duration_nsec);
+ ofr->idle_timeout = htons(fr->idle_timeout);
+ ofr->packet_count = htonll(unknown_to_zero(fr->packet_count));
+ ofr->byte_count = htonll(unknown_to_zero(fr->byte_count));
+ break;
+ }
+
+ case OFPUTIL_P_OF10_NXM:
+ case OFPUTIL_P_OF10_NXM_TID: {
+ struct nx_flow_removed *nfr;
+ int match_len;
+
+ msg = ofpraw_alloc_xid(OFPRAW_NXT_FLOW_REMOVED, OFP10_VERSION,
+ htonl(0), NXM_TYPICAL_LEN);
+ nfr = ofpbuf_put_zeros(msg, sizeof *nfr);
+ match_len = nx_put_match(msg, &fr->match, 0, 0);
+
+ nfr = msg->l3;
+ nfr->cookie = fr->cookie;
+ nfr->priority = htons(fr->priority);
+ nfr->reason = fr->reason;
+ nfr->table_id = fr->table_id + 1;
+ nfr->duration_sec = htonl(fr->duration_sec);
+ nfr->duration_nsec = htonl(fr->duration_nsec);
+ nfr->idle_timeout = htons(fr->idle_timeout);
+ nfr->match_len = htons(match_len);
+ nfr->packet_count = htonll(fr->packet_count);
+ nfr->byte_count = htonll(fr->byte_count);
+ break;
+ }
+
+ default:
+ NOT_REACHED();
+ }
+
+ return msg;
+}
+
+static void
+ofputil_decode_packet_in_finish(struct ofputil_packet_in *pin,
+ struct match *match, struct ofpbuf *b)
+{
+ pin->packet = b->data;
+ pin->packet_len = b->size;
+
+ pin->fmd.in_port = match->flow.in_port.ofp_port;
+ pin->fmd.tun_id = match->flow.tunnel.tun_id;
+ pin->fmd.tun_src = match->flow.tunnel.ip_src;
+ pin->fmd.tun_dst = match->flow.tunnel.ip_dst;
+ pin->fmd.metadata = match->flow.metadata;
+ memcpy(pin->fmd.regs, match->flow.regs, sizeof pin->fmd.regs);
+ pin->fmd.pkt_mark = match->flow.pkt_mark;
+}
+
+enum ofperr
+ofputil_decode_packet_in(struct ofputil_packet_in *pin,
+ const struct ofp_header *oh)
+{
+ enum ofpraw raw;
+ struct ofpbuf b;
+
+ memset(pin, 0, sizeof *pin);
+ pin->cookie = OVS_BE64_MAX;
+
+ ofpbuf_use_const(&b, oh, ntohs(oh->length));
+ raw = ofpraw_pull_assert(&b);
+ if (raw == OFPRAW_OFPT13_PACKET_IN || raw == OFPRAW_OFPT12_PACKET_IN) {
+ const struct ofp13_packet_in *opi;
+ struct match match;
+ int error;
+ size_t packet_in_size;
+
+ if (raw == OFPRAW_OFPT12_PACKET_IN) {
+ packet_in_size = sizeof (struct ofp12_packet_in);
+ } else {
+ packet_in_size = sizeof (struct ofp13_packet_in);
+ }
+
+ opi = ofpbuf_pull(&b, packet_in_size);
+ error = oxm_pull_match_loose(&b, &match);
+ if (error) {
+ return error;
+ }
+
+ if (!ofpbuf_try_pull(&b, 2)) {
+ return OFPERR_OFPBRC_BAD_LEN;
+ }
+
+ pin->reason = opi->pi.reason;
+ pin->table_id = opi->pi.table_id;
+ pin->buffer_id = ntohl(opi->pi.buffer_id);
+ pin->total_len = ntohs(opi->pi.total_len);
+
+ if (raw == OFPRAW_OFPT13_PACKET_IN) {
+ pin->cookie = opi->cookie;
+ }
+
+ ofputil_decode_packet_in_finish(pin, &match, &b);
+ } else if (raw == OFPRAW_OFPT10_PACKET_IN) {
+ const struct ofp10_packet_in *opi;
+
+ opi = ofpbuf_pull(&b, offsetof(struct ofp10_packet_in, data));
+
+ pin->packet = opi->data;
+ pin->packet_len = b.size;
+
+ pin->fmd.in_port = u16_to_ofp(ntohs(opi->in_port));
+ pin->reason = opi->reason;
+ pin->buffer_id = ntohl(opi->buffer_id);
+ pin->total_len = ntohs(opi->total_len);
+ } else if (raw == OFPRAW_NXT_PACKET_IN) {
+ const struct nx_packet_in *npi;
+ struct match match;
+ int error;
+
+ npi = ofpbuf_pull(&b, sizeof *npi);
+ error = nx_pull_match_loose(&b, ntohs(npi->match_len), &match, NULL,
+ NULL);
+ if (error) {
+ return error;
+ }
+
+ if (!ofpbuf_try_pull(&b, 2)) {
+ return OFPERR_OFPBRC_BAD_LEN;
+ }
+
+ pin->reason = npi->reason;
+ pin->table_id = npi->table_id;
+ pin->cookie = npi->cookie;
+
+ pin->buffer_id = ntohl(npi->buffer_id);
+ pin->total_len = ntohs(npi->total_len);
+
+ ofputil_decode_packet_in_finish(pin, &match, &b);
+ } else {
+ NOT_REACHED();
+ }
+
+ return 0;
+}
+
+static void
+ofputil_packet_in_to_match(const struct ofputil_packet_in *pin,
+ struct match *match)
+{
+ int i;
+
+ match_init_catchall(match);
+ if (pin->fmd.tun_id != htonll(0)) {
+ match_set_tun_id(match, pin->fmd.tun_id);
+ }
+ if (pin->fmd.tun_src != htonl(0)) {
+ match_set_tun_src(match, pin->fmd.tun_src);
+ }
+ if (pin->fmd.tun_dst != htonl(0)) {
+ match_set_tun_dst(match, pin->fmd.tun_dst);
+ }
+ if (pin->fmd.metadata != htonll(0)) {
+ match_set_metadata(match, pin->fmd.metadata);
+ }
+
+ for (i = 0; i < FLOW_N_REGS; i++) {
+ if (pin->fmd.regs[i]) {
+ match_set_reg(match, i, pin->fmd.regs[i]);
+ }
+ }
+
+ if (pin->fmd.pkt_mark != 0) {
+ match_set_pkt_mark(match, pin->fmd.pkt_mark);
+ }
+
+ match_set_in_port(match, pin->fmd.in_port);
+}
+
+/* Converts abstract ofputil_packet_in 'pin' into a PACKET_IN message
+ * in the format specified by 'packet_in_format'. */
+struct ofpbuf *
+ofputil_encode_packet_in(const struct ofputil_packet_in *pin,
+ enum ofputil_protocol protocol,
+ enum nx_packet_in_format packet_in_format)
+{
+ struct ofpbuf *packet;
+
+ /* Add OFPT_PACKET_IN. */
+ if (protocol == OFPUTIL_P_OF13_OXM || protocol == OFPUTIL_P_OF12_OXM) {
+ struct ofp13_packet_in *opi;
+ struct match match;
+ enum ofpraw packet_in_raw;
+ enum ofp_version packet_in_version;
+ size_t packet_in_size;
+
+ if (protocol == OFPUTIL_P_OF12_OXM) {
+ packet_in_raw = OFPRAW_OFPT12_PACKET_IN;
+ packet_in_version = OFP12_VERSION;
+ packet_in_size = sizeof (struct ofp12_packet_in);
+ } else {
+ packet_in_raw = OFPRAW_OFPT13_PACKET_IN;
+ packet_in_version = OFP13_VERSION;
+ packet_in_size = sizeof (struct ofp13_packet_in);
+ }
+
+ ofputil_packet_in_to_match(pin, &match);
+
+ /* The final argument is just an estimate of the space required. */
+ packet = ofpraw_alloc_xid(packet_in_raw, packet_in_version,
+ htonl(0), (sizeof(struct flow_metadata) * 2
+ + 2 + pin->packet_len));
+ ofpbuf_put_zeros(packet, packet_in_size);
+ oxm_put_match(packet, &match);
+ ofpbuf_put_zeros(packet, 2);
+ ofpbuf_put(packet, pin->packet, pin->packet_len);
+
+ opi = packet->l3;
+ opi->pi.buffer_id = htonl(pin->buffer_id);
+ opi->pi.total_len = htons(pin->total_len);
+ opi->pi.reason = pin->reason;
+ opi->pi.table_id = pin->table_id;
+ if (protocol == OFPUTIL_P_OF13_OXM) {
+ opi->cookie = pin->cookie;
+ }
+ } else if (packet_in_format == NXPIF_OPENFLOW10) {
+ struct ofp10_packet_in *opi;
+
+ packet = ofpraw_alloc_xid(OFPRAW_OFPT10_PACKET_IN, OFP10_VERSION,
+ htonl(0), pin->packet_len);
+ opi = ofpbuf_put_zeros(packet, offsetof(struct ofp10_packet_in, data));
+ opi->total_len = htons(pin->total_len);
+ opi->in_port = htons(ofp_to_u16(pin->fmd.in_port));
+ opi->reason = pin->reason;
+ opi->buffer_id = htonl(pin->buffer_id);
+
+ ofpbuf_put(packet, pin->packet, pin->packet_len);
+ } else if (packet_in_format == NXPIF_NXM) {
+ struct nx_packet_in *npi;
+ struct match match;
+ size_t match_len;
+
+ ofputil_packet_in_to_match(pin, &match);
+
+ /* The final argument is just an estimate of the space required. */
+ packet = ofpraw_alloc_xid(OFPRAW_NXT_PACKET_IN, OFP10_VERSION,
+ htonl(0), (sizeof(struct flow_metadata) * 2
+ + 2 + pin->packet_len));
+ ofpbuf_put_zeros(packet, sizeof *npi);
+ match_len = nx_put_match(packet, &match, 0, 0);
+ ofpbuf_put_zeros(packet, 2);
+ ofpbuf_put(packet, pin->packet, pin->packet_len);
+
+ npi = packet->l3;
+ npi->buffer_id = htonl(pin->buffer_id);
+ npi->total_len = htons(pin->total_len);
+ npi->reason = pin->reason;
+ npi->table_id = pin->table_id;
+ npi->cookie = pin->cookie;
+ npi->match_len = htons(match_len);
+ } else {
+ NOT_REACHED();
+ }
+ ofpmsg_update_length(packet);
+
+ return packet;
+}
+
+/* Returns a string form of 'reason'. The return value is either a statically
+ * allocated constant string or the 'bufsize'-byte buffer 'reasonbuf'.
+ * 'bufsize' should be at least OFPUTIL_PACKET_IN_REASON_BUFSIZE. */
+const char *
+ofputil_packet_in_reason_to_string(enum ofp_packet_in_reason reason,
+ char *reasonbuf, size_t bufsize)
+{
+ switch (reason) {
+ case OFPR_NO_MATCH:
+ return "no_match";
+ case OFPR_ACTION:
+ return "action";
+ case OFPR_INVALID_TTL:
+ return "invalid_ttl";
+
+ case OFPR_N_REASONS:
+ default:
+ snprintf(reasonbuf, bufsize, "%d", (int) reason);
+ return reasonbuf;
+ }
+}
+
+bool
+ofputil_packet_in_reason_from_string(const char *s,
+ enum ofp_packet_in_reason *reason)
+{
+ int i;
+
+ for (i = 0; i < OFPR_N_REASONS; i++) {
+ char reasonbuf[OFPUTIL_PACKET_IN_REASON_BUFSIZE];
+ const char *reason_s;
+
+ reason_s = ofputil_packet_in_reason_to_string(i, reasonbuf,
+ sizeof reasonbuf);
+ if (!strcasecmp(s, reason_s)) {
+ *reason = i;
+ return true;
+ }
+ }
+ 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_header *oh,
+ struct ofpbuf *ofpacts)
+{
+ enum ofpraw raw;
+ struct ofpbuf b;
+
+ ofpbuf_use_const(&b, oh, ntohs(oh->length));
+ raw = ofpraw_pull_assert(&b);
+
+ if (raw == OFPRAW_OFPT11_PACKET_OUT) {
+ enum ofperr error;
+ const struct ofp11_packet_out *opo = ofpbuf_pull(&b, sizeof *opo);
+
+ po->buffer_id = ntohl(opo->buffer_id);
+ error = ofputil_port_from_ofp11(opo->in_port, &po->in_port);
+ if (error) {
+ return error;
+ }
+
+ error = ofpacts_pull_openflow_actions(&b, ntohs(opo->actions_len),
+ oh->version, ofpacts);
+ if (error) {
+ return error;
+ }
+ } else if (raw == OFPRAW_OFPT10_PACKET_OUT) {
+ enum ofperr error;
+ const struct ofp10_packet_out *opo = ofpbuf_pull(&b, sizeof *opo);
+
+ po->buffer_id = ntohl(opo->buffer_id);
+ po->in_port = u16_to_ofp(ntohs(opo->in_port));
+
+ error = ofpacts_pull_openflow_actions(&b, ntohs(opo->actions_len),
+ oh->version, ofpacts);
+ if (error) {
+ return error;
+ }
+ } else {
+ NOT_REACHED();
+ }
+
+ if (ofp_to_u16(po->in_port) >= ofp_to_u16(OFPP_MAX)
+ && po->in_port != OFPP_LOCAL
+ && po->in_port != OFPP_NONE && po->in_port != OFPP_CONTROLLER) {
+ VLOG_WARN_RL(&bad_ofmsg_rl, "packet-out has bad input port %#"PRIx16,
+ po->in_port);
+ return OFPERR_OFPBRC_BAD_PORT;
+ }
+
+ po->ofpacts = ofpacts->data;
+ po->ofpacts_len = ofpacts->size;
+
+ if (po->buffer_id == UINT32_MAX) {
+ po->packet = b.data;
+ po->packet_len = b.size;
+ } else {
+ po->packet = NULL;
+ po->packet_len = 0;
+ }
+
+ return 0;
+}
+\f
+/* ofputil_phy_port */
+
+/* NETDEV_F_* to and from OFPPF_* and OFPPF10_*. */
+BUILD_ASSERT_DECL((int) NETDEV_F_10MB_HD == OFPPF_10MB_HD); /* bit 0 */
+BUILD_ASSERT_DECL((int) NETDEV_F_10MB_FD == OFPPF_10MB_FD); /* bit 1 */
+BUILD_ASSERT_DECL((int) NETDEV_F_100MB_HD == OFPPF_100MB_HD); /* bit 2 */
+BUILD_ASSERT_DECL((int) NETDEV_F_100MB_FD == OFPPF_100MB_FD); /* bit 3 */
+BUILD_ASSERT_DECL((int) NETDEV_F_1GB_HD == OFPPF_1GB_HD); /* bit 4 */
+BUILD_ASSERT_DECL((int) NETDEV_F_1GB_FD == OFPPF_1GB_FD); /* bit 5 */
+BUILD_ASSERT_DECL((int) NETDEV_F_10GB_FD == OFPPF_10GB_FD); /* bit 6 */
+
+/* NETDEV_F_ bits 11...15 are OFPPF10_ bits 7...11: */
+BUILD_ASSERT_DECL((int) NETDEV_F_COPPER == (OFPPF10_COPPER << 4));
+BUILD_ASSERT_DECL((int) NETDEV_F_FIBER == (OFPPF10_FIBER << 4));
+BUILD_ASSERT_DECL((int) NETDEV_F_AUTONEG == (OFPPF10_AUTONEG << 4));
+BUILD_ASSERT_DECL((int) NETDEV_F_PAUSE == (OFPPF10_PAUSE << 4));
+BUILD_ASSERT_DECL((int) NETDEV_F_PAUSE_ASYM == (OFPPF10_PAUSE_ASYM << 4));
+
+static enum netdev_features
+netdev_port_features_from_ofp10(ovs_be32 ofp10_)
+{
+ uint32_t ofp10 = ntohl(ofp10_);
+ return (ofp10 & 0x7f) | ((ofp10 & 0xf80) << 4);
+}
+
+static ovs_be32
+netdev_port_features_to_ofp10(enum netdev_features features)
+{
+ return htonl((features & 0x7f) | ((features & 0xf800) >> 4));
+}
+
+BUILD_ASSERT_DECL((int) NETDEV_F_10MB_HD == OFPPF_10MB_HD); /* bit 0 */
+BUILD_ASSERT_DECL((int) NETDEV_F_10MB_FD == OFPPF_10MB_FD); /* bit 1 */
+BUILD_ASSERT_DECL((int) NETDEV_F_100MB_HD == OFPPF_100MB_HD); /* bit 2 */
+BUILD_ASSERT_DECL((int) NETDEV_F_100MB_FD == OFPPF_100MB_FD); /* bit 3 */
+BUILD_ASSERT_DECL((int) NETDEV_F_1GB_HD == OFPPF_1GB_HD); /* bit 4 */
+BUILD_ASSERT_DECL((int) NETDEV_F_1GB_FD == OFPPF_1GB_FD); /* bit 5 */
+BUILD_ASSERT_DECL((int) NETDEV_F_10GB_FD == OFPPF_10GB_FD); /* bit 6 */
+BUILD_ASSERT_DECL((int) NETDEV_F_40GB_FD == OFPPF11_40GB_FD); /* bit 7 */
+BUILD_ASSERT_DECL((int) NETDEV_F_100GB_FD == OFPPF11_100GB_FD); /* bit 8 */
+BUILD_ASSERT_DECL((int) NETDEV_F_1TB_FD == OFPPF11_1TB_FD); /* bit 9 */
+BUILD_ASSERT_DECL((int) NETDEV_F_OTHER == OFPPF11_OTHER); /* bit 10 */
+BUILD_ASSERT_DECL((int) NETDEV_F_COPPER == OFPPF11_COPPER); /* bit 11 */
+BUILD_ASSERT_DECL((int) NETDEV_F_FIBER == OFPPF11_FIBER); /* bit 12 */
+BUILD_ASSERT_DECL((int) NETDEV_F_AUTONEG == OFPPF11_AUTONEG); /* bit 13 */
+BUILD_ASSERT_DECL((int) NETDEV_F_PAUSE == OFPPF11_PAUSE); /* bit 14 */
+BUILD_ASSERT_DECL((int) NETDEV_F_PAUSE_ASYM == OFPPF11_PAUSE_ASYM);/* bit 15 */
+
+static enum netdev_features
+netdev_port_features_from_ofp11(ovs_be32 ofp11)
+{
+ return ntohl(ofp11) & 0xffff;
+}
+
+static ovs_be32
+netdev_port_features_to_ofp11(enum netdev_features features)
+{
+ return htonl(features & 0xffff);
+}
+
+static enum ofperr
+ofputil_decode_ofp10_phy_port(struct ofputil_phy_port *pp,
+ const struct ofp10_phy_port *opp)
+{
+ memset(pp, 0, sizeof *pp);
+
+ pp->port_no = u16_to_ofp(ntohs(opp->port_no));
+ memcpy(pp->hw_addr, opp->hw_addr, OFP_ETH_ALEN);
+ ovs_strlcpy(pp->name, opp->name, OFP_MAX_PORT_NAME_LEN);
+
+ pp->config = ntohl(opp->config) & OFPPC10_ALL;
+ pp->state = ntohl(opp->state) & OFPPS10_ALL;
+
+ pp->curr = netdev_port_features_from_ofp10(opp->curr);
+ pp->advertised = netdev_port_features_from_ofp10(opp->advertised);
+ pp->supported = netdev_port_features_from_ofp10(opp->supported);
+ pp->peer = netdev_port_features_from_ofp10(opp->peer);
+
+ pp->curr_speed = netdev_features_to_bps(pp->curr, 0) / 1000;
+ pp->max_speed = netdev_features_to_bps(pp->supported, 0) / 1000;
+
+ return 0;
+}
+
+static enum ofperr
+ofputil_decode_ofp11_port(struct ofputil_phy_port *pp,
+ const struct ofp11_port *op)
+{
+ enum ofperr error;
+
+ memset(pp, 0, sizeof *pp);
+
+ error = ofputil_port_from_ofp11(op->port_no, &pp->port_no);
+ if (error) {
+ return error;
+ }
+ memcpy(pp->hw_addr, op->hw_addr, OFP_ETH_ALEN);
+ ovs_strlcpy(pp->name, op->name, OFP_MAX_PORT_NAME_LEN);
+
+ pp->config = ntohl(op->config) & OFPPC11_ALL;
+ pp->state = ntohl(op->state) & OFPPC11_ALL;
+
+ pp->curr = netdev_port_features_from_ofp11(op->curr);
+ pp->advertised = netdev_port_features_from_ofp11(op->advertised);
+ pp->supported = netdev_port_features_from_ofp11(op->supported);
+ pp->peer = netdev_port_features_from_ofp11(op->peer);
+
+ pp->curr_speed = ntohl(op->curr_speed);
+ pp->max_speed = ntohl(op->max_speed);
+
+ return 0;
+}
+
+static size_t
+ofputil_get_phy_port_size(enum ofp_version ofp_version)
+{
+ switch (ofp_version) {
+ case OFP10_VERSION:
+ return sizeof(struct ofp10_phy_port);
+ case OFP11_VERSION:
+ case OFP12_VERSION:
+ case OFP13_VERSION:
+ return sizeof(struct ofp11_port);
+ default:
+ NOT_REACHED();
+ }
+}
+
+static void
+ofputil_encode_ofp10_phy_port(const struct ofputil_phy_port *pp,