+ ofpbuf_use_const(b, oh, ntohs(oh->length));
+ raw = ofpraw_pull_assert(b);
+
+ osf = ofpbuf_pull(b, sizeof *osf);
+ features->datapath_id = ntohll(osf->datapath_id);
+ features->n_buffers = ntohl(osf->n_buffers);
+ features->n_tables = osf->n_tables;
+
+ features->capabilities = ntohl(osf->capabilities) &
+ ofputil_capabilities_mask(oh->version);
+
+ if (b->size % ofputil_get_phy_port_size(oh->version)) {
+ return OFPERR_OFPBRC_BAD_LEN;
+ }
+
+ if (raw == OFPRAW_OFPT10_FEATURES_REPLY) {
+ if (osf->capabilities & htonl(OFPC10_STP)) {
+ features->capabilities |= OFPUTIL_C_STP;
+ }
+ features->actions = decode_action_bits(osf->actions, of10_action_bits);
+ } else if (raw == OFPRAW_OFPT11_FEATURES_REPLY) {
+ if (osf->capabilities & htonl(OFPC11_GROUP_STATS)) {
+ features->capabilities |= OFPUTIL_C_GROUP_STATS;
+ }
+ features->actions = 0;
+ } else {
+ return OFPERR_OFPBRC_BAD_VERSION;
+ }
+
+ return 0;
+}
+
+/* Returns true if the maximum number of ports are in 'oh'. */
+static bool
+max_ports_in_features(const struct ofp_header *oh)
+{
+ size_t pp_size = ofputil_get_phy_port_size(oh->version);
+ return ntohs(oh->length) + pp_size > UINT16_MAX;
+}
+
+/* Given a buffer 'b' that contains a Features Reply message, checks if
+ * it contains the maximum number of ports that will fit. If so, it
+ * returns true and removes the ports from the message. The caller
+ * should then send an OFPST_PORT_DESC stats request to get the ports,
+ * since the switch may have more ports than could be represented in the
+ * Features Reply. Otherwise, returns false.
+ */
+bool
+ofputil_switch_features_ports_trunc(struct ofpbuf *b)
+{
+ struct ofp_header *oh = b->data;
+
+ if (max_ports_in_features(oh)) {
+ /* Remove all the ports. */
+ b->size = (sizeof(struct ofp_header)
+ + sizeof(struct ofp_switch_features));
+ ofpmsg_update_length(b);
+
+ return true;
+ }
+
+ return false;
+}
+
+static ovs_be32
+encode_action_bits(enum ofputil_action_bitmap ofputil_actions,
+ const struct ofputil_action_bit_translation *x)
+{
+ uint32_t of_actions;
+
+ of_actions = 0;
+ for (; x->ofputil_bit; x++) {
+ if (ofputil_actions & x->ofputil_bit) {
+ of_actions |= 1 << x->of_bit;
+ }
+ }
+ return htonl(of_actions);
+}
+
+/* Returns a buffer owned by the caller that encodes 'features' in the format
+ * required by 'protocol' with the given 'xid'. The caller should append port
+ * information to the buffer with subsequent calls to
+ * ofputil_put_switch_features_port(). */
+struct ofpbuf *
+ofputil_encode_switch_features(const struct ofputil_switch_features *features,
+ enum ofputil_protocol protocol, ovs_be32 xid)
+{
+ struct ofp_switch_features *osf;
+ struct ofpbuf *b;
+ enum ofp_version version;
+ enum ofpraw raw;
+
+ version = ofputil_protocol_to_ofp_version(protocol);
+ switch (version) {
+ case OFP10_VERSION:
+ raw = OFPRAW_OFPT10_FEATURES_REPLY;
+ break;
+ case OFP11_VERSION:
+ case OFP12_VERSION:
+ raw = OFPRAW_OFPT11_FEATURES_REPLY;
+ break;
+ default:
+ NOT_REACHED();
+ }
+ b = ofpraw_alloc_xid(raw, version, xid, 0);
+ osf = ofpbuf_put_zeros(b, sizeof *osf);
+ osf->datapath_id = htonll(features->datapath_id);
+ osf->n_buffers = htonl(features->n_buffers);
+ osf->n_tables = features->n_tables;
+
+ osf->capabilities = htonl(features->capabilities & OFPC_COMMON);
+ osf->capabilities = htonl(features->capabilities &
+ ofputil_capabilities_mask(version));
+ switch (version) {
+ case OFP10_VERSION:
+ if (features->capabilities & OFPUTIL_C_STP) {
+ osf->capabilities |= htonl(OFPC10_STP);
+ }
+ osf->actions = encode_action_bits(features->actions, of10_action_bits);
+ break;
+ case OFP11_VERSION:
+ case OFP12_VERSION:
+ if (features->capabilities & OFPUTIL_C_GROUP_STATS) {
+ osf->capabilities |= htonl(OFPC11_GROUP_STATS);
+ }
+ break;
+ default:
+ NOT_REACHED();
+ }
+
+ return b;
+}
+
+/* Encodes 'pp' into the format required by the switch_features message already
+ * in 'b', which should have been returned by ofputil_encode_switch_features(),
+ * and appends the encoded version to 'b'. */
+void
+ofputil_put_switch_features_port(const struct ofputil_phy_port *pp,
+ struct ofpbuf *b)
+{
+ const struct ofp_header *oh = b->data;
+
+ ofputil_put_phy_port(oh->version, pp, b);
+}
+\f
+/* ofputil_port_status */
+
+/* Decodes the OpenFlow "port status" message in '*ops' into an abstract form
+ * in '*ps'. Returns 0 if successful, otherwise an OFPERR_* value. */
+enum ofperr
+ofputil_decode_port_status(const struct ofp_header *oh,
+ struct ofputil_port_status *ps)
+{
+ const struct ofp_port_status *ops;
+ struct ofpbuf b;
+ int retval;
+
+ ofpbuf_use_const(&b, oh, ntohs(oh->length));
+ ofpraw_pull_assert(&b);
+ ops = ofpbuf_pull(&b, sizeof *ops);
+
+ if (ops->reason != OFPPR_ADD &&
+ ops->reason != OFPPR_DELETE &&
+ ops->reason != OFPPR_MODIFY) {
+ return OFPERR_NXBRC_BAD_REASON;
+ }
+ ps->reason = ops->reason;
+
+ retval = ofputil_pull_phy_port(oh->version, &b, &ps->desc);
+ assert(retval != EOF);
+ return retval;
+}
+
+/* Converts the abstract form of a "port status" message in '*ps' into an
+ * OpenFlow message suitable for 'protocol', and returns that encoded form in
+ * a buffer owned by the caller. */
+struct ofpbuf *
+ofputil_encode_port_status(const struct ofputil_port_status *ps,
+ enum ofputil_protocol protocol)
+{
+ struct ofp_port_status *ops;
+ struct ofpbuf *b;
+ enum ofp_version version;
+ enum ofpraw raw;
+
+ version = ofputil_protocol_to_ofp_version(protocol);
+ switch (version) {
+ case OFP10_VERSION:
+ raw = OFPRAW_OFPT10_PORT_STATUS;
+ break;
+
+ case OFP11_VERSION:
+ case OFP12_VERSION:
+ raw = OFPRAW_OFPT11_PORT_STATUS;
+ break;
+
+ default:
+ NOT_REACHED();
+ }
+
+ b = ofpraw_alloc_xid(raw, version, htonl(0), 0);
+ ops = ofpbuf_put_zeros(b, sizeof *ops);
+ ops->reason = ps->reason;
+ ofputil_put_phy_port(version, &ps->desc, b);
+ ofpmsg_update_length(b);
+ return b;
+}
+\f
+/* ofputil_port_mod */
+
+/* Decodes the OpenFlow "port mod" message in '*oh' into an abstract form in
+ * '*pm'. Returns 0 if successful, otherwise an OFPERR_* value. */
+enum ofperr
+ofputil_decode_port_mod(const struct ofp_header *oh,
+ struct ofputil_port_mod *pm)
+{
+ enum ofpraw raw;
+ struct ofpbuf b;
+
+ ofpbuf_use_const(&b, oh, ntohs(oh->length));
+ raw = ofpraw_pull_assert(&b);
+
+ if (raw == OFPRAW_OFPT10_PORT_MOD) {
+ const struct ofp10_port_mod *opm = b.data;
+
+ pm->port_no = ntohs(opm->port_no);
+ memcpy(pm->hw_addr, opm->hw_addr, ETH_ADDR_LEN);
+ pm->config = ntohl(opm->config) & OFPPC10_ALL;
+ pm->mask = ntohl(opm->mask) & OFPPC10_ALL;
+ pm->advertise = netdev_port_features_from_ofp10(opm->advertise);
+ } else if (raw == OFPRAW_OFPT11_PORT_MOD) {
+ const struct ofp11_port_mod *opm = b.data;
+ enum ofperr error;
+
+ error = ofputil_port_from_ofp11(opm->port_no, &pm->port_no);
+ if (error) {
+ return error;
+ }
+
+ memcpy(pm->hw_addr, opm->hw_addr, ETH_ADDR_LEN);
+ pm->config = ntohl(opm->config) & OFPPC11_ALL;
+ pm->mask = ntohl(opm->mask) & OFPPC11_ALL;
+ pm->advertise = netdev_port_features_from_ofp11(opm->advertise);
+ } else {
+ return OFPERR_OFPBRC_BAD_TYPE;
+ }
+
+ pm->config &= pm->mask;
+ return 0;
+}
+
+/* Converts the abstract form of a "port mod" message in '*pm' into an OpenFlow
+ * message suitable for 'protocol', and returns that encoded form in a buffer
+ * owned by the caller. */
+struct ofpbuf *
+ofputil_encode_port_mod(const struct ofputil_port_mod *pm,
+ enum ofputil_protocol protocol)
+{
+ enum ofp_version ofp_version = ofputil_protocol_to_ofp_version(protocol);
+ struct ofpbuf *b;
+
+ switch (ofp_version) {
+ case OFP10_VERSION: {
+ struct ofp10_port_mod *opm;
+
+ b = ofpraw_alloc(OFPRAW_OFPT10_PORT_MOD, ofp_version, 0);
+ opm = ofpbuf_put_zeros(b, sizeof *opm);
+ opm->port_no = htons(pm->port_no);
+ memcpy(opm->hw_addr, pm->hw_addr, ETH_ADDR_LEN);
+ opm->config = htonl(pm->config & OFPPC10_ALL);
+ opm->mask = htonl(pm->mask & OFPPC10_ALL);
+ opm->advertise = netdev_port_features_to_ofp10(pm->advertise);
+ break;
+ }
+
+ case OFP11_VERSION:
+ case OFP12_VERSION: {
+ struct ofp11_port_mod *opm;
+
+ b = ofpraw_alloc(OFPRAW_OFPT11_PORT_MOD, ofp_version, 0);
+ opm = ofpbuf_put_zeros(b, sizeof *opm);
+ opm->port_no = ofputil_port_to_ofp11(pm->port_no);
+ memcpy(opm->hw_addr, pm->hw_addr, ETH_ADDR_LEN);
+ opm->config = htonl(pm->config & OFPPC11_ALL);
+ opm->mask = htonl(pm->mask & OFPPC11_ALL);
+ opm->advertise = netdev_port_features_to_ofp11(pm->advertise);
+ break;
+ }
+
+ default:
+ NOT_REACHED();
+ }
+
+ return b;
+}
+\f
+/* Table stats. */
+
+static void
+ofputil_put_ofp10_table_stats(const struct ofp12_table_stats *in,
+ struct ofpbuf *buf)
+{
+ struct wc_map {
+ enum ofp_flow_wildcards wc10;
+ enum oxm12_ofb_match_fields mf12;
+ };
+
+ static const struct wc_map wc_map[] = {
+ { OFPFW10_IN_PORT, OFPXMT12_OFB_IN_PORT },
+ { OFPFW10_DL_VLAN, OFPXMT12_OFB_VLAN_VID },
+ { OFPFW10_DL_SRC, OFPXMT12_OFB_ETH_SRC },
+ { OFPFW10_DL_DST, OFPXMT12_OFB_ETH_DST},
+ { OFPFW10_DL_TYPE, OFPXMT12_OFB_ETH_TYPE },
+ { OFPFW10_NW_PROTO, OFPXMT12_OFB_IP_PROTO },
+ { OFPFW10_TP_SRC, OFPXMT12_OFB_TCP_SRC },
+ { OFPFW10_TP_DST, OFPXMT12_OFB_TCP_DST },
+ { OFPFW10_NW_SRC_MASK, OFPXMT12_OFB_IPV4_SRC },
+ { OFPFW10_NW_DST_MASK, OFPXMT12_OFB_IPV4_DST },
+ { OFPFW10_DL_VLAN_PCP, OFPXMT12_OFB_VLAN_PCP },
+ { OFPFW10_NW_TOS, OFPXMT12_OFB_IP_DSCP },
+ };
+
+ struct ofp10_table_stats *out;
+ const struct wc_map *p;
+
+ out = ofpbuf_put_uninit(buf, sizeof *out);
+ out->table_id = in->table_id;
+ strcpy(out->name, in->name);
+ out->wildcards = 0;
+ for (p = wc_map; p < &wc_map[ARRAY_SIZE(wc_map)]; p++) {
+ if (in->wildcards & htonll(1ULL << p->mf12)) {
+ out->wildcards |= htonl(p->wc10);
+ }
+ }
+ out->max_entries = in->max_entries;
+ out->active_count = in->active_count;
+ put_32aligned_be64(&out->lookup_count, in->lookup_count);
+ put_32aligned_be64(&out->matched_count, in->matched_count);
+}
+
+static ovs_be32
+oxm12_to_ofp11_flow_match_fields(ovs_be64 oxm12)
+{
+ struct map {
+ enum ofp11_flow_match_fields fmf11;
+ enum oxm12_ofb_match_fields mf12;
+ };
+
+ static const struct map map[] = {
+ { OFPFMF11_IN_PORT, OFPXMT12_OFB_IN_PORT },
+ { OFPFMF11_DL_VLAN, OFPXMT12_OFB_VLAN_VID },
+ { OFPFMF11_DL_VLAN_PCP, OFPXMT12_OFB_VLAN_PCP },
+ { OFPFMF11_DL_TYPE, OFPXMT12_OFB_ETH_TYPE },
+ { OFPFMF11_NW_TOS, OFPXMT12_OFB_IP_DSCP },
+ { OFPFMF11_NW_PROTO, OFPXMT12_OFB_IP_PROTO },
+ { OFPFMF11_TP_SRC, OFPXMT12_OFB_TCP_SRC },
+ { OFPFMF11_TP_DST, OFPXMT12_OFB_TCP_DST },
+ { OFPFMF11_MPLS_LABEL, OFPXMT12_OFB_MPLS_LABEL },
+ { OFPFMF11_MPLS_TC, OFPXMT12_OFB_MPLS_TC },
+ /* I don't know what OFPFMF11_TYPE means. */
+ { OFPFMF11_DL_SRC, OFPXMT12_OFB_ETH_SRC },
+ { OFPFMF11_DL_DST, OFPXMT12_OFB_ETH_DST },
+ { OFPFMF11_NW_SRC, OFPXMT12_OFB_IPV4_SRC },
+ { OFPFMF11_NW_DST, OFPXMT12_OFB_IPV4_DST },
+ { OFPFMF11_METADATA, OFPXMT12_OFB_METADATA },
+ };
+
+ const struct map *p;
+ uint32_t fmf11;
+
+ fmf11 = 0;
+ for (p = map; p < &map[ARRAY_SIZE(map)]; p++) {
+ if (oxm12 & htonll(1ULL << p->mf12)) {
+ fmf11 |= p->fmf11;
+ }
+ }
+ return htonl(fmf11);
+}
+
+static void
+ofputil_put_ofp11_table_stats(const struct ofp12_table_stats *in,
+ struct ofpbuf *buf)
+{
+ struct ofp11_table_stats *out;
+
+ out = ofpbuf_put_uninit(buf, sizeof *out);
+ out->table_id = in->table_id;
+ strcpy(out->name, in->name);
+ out->wildcards = oxm12_to_ofp11_flow_match_fields(in->wildcards);
+ out->match = oxm12_to_ofp11_flow_match_fields(in->match);
+ out->instructions = in->instructions;
+ out->write_actions = in->write_actions;
+ out->apply_actions = in->apply_actions;
+ out->config = in->config;
+ out->max_entries = in->max_entries;
+ out->active_count = in->active_count;
+ out->lookup_count = in->lookup_count;
+ out->matched_count = in->matched_count;
+}
+
+struct ofpbuf *
+ofputil_encode_table_stats_reply(const struct ofp12_table_stats stats[], int n,
+ const struct ofp_header *request)
+{
+ struct ofpbuf *reply;
+ int i;
+
+ reply = ofpraw_alloc_stats_reply(request, n * sizeof *stats);
+
+ switch ((enum ofp_version) request->version) {
+ case OFP10_VERSION:
+ for (i = 0; i < n; i++) {
+ ofputil_put_ofp10_table_stats(&stats[i], reply);
+ }
+ break;
+
+ case OFP11_VERSION:
+ for (i = 0; i < n; i++) {
+ ofputil_put_ofp11_table_stats(&stats[i], reply);
+ }
+ break;
+
+ case OFP12_VERSION:
+ ofpbuf_put(reply, stats, n * sizeof *stats);
+ break;
+
+ default:
+ NOT_REACHED();
+ }
+
+ return reply;
+}
+\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;
+ ofpraw_pull_assert(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), &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) {
+ ofpraw_put(OFPRAW_NXST_FLOW_MONITOR_REQUEST, OFP10_VERSION, msg);
+ }
+
+ start_ofs = msg->size;
+ ofpbuf_put_zeros(msg, sizeof *nfmr);
+ match_len = nx_put_match(msg, &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 match.
+ *
+ * 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;
+ ofpraw_pull_assert(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;
+ update->priority = ntohs(nfuf->priority);
+
+ error = nx_pull_match(msg, match_len, 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)
+{
+ const struct nx_flow_monitor_cancel *cancel = ofpmsg_body(oh);
+
+ return ntohl(cancel->id);
+}
+
+struct ofpbuf *
+ofputil_encode_flow_monitor_cancel(uint32_t id)
+{
+ struct nx_flow_monitor_cancel *nfmc;
+ struct ofpbuf *msg;
+
+ msg = ofpraw_alloc(OFPRAW_NXT_FLOW_MONITOR_CANCEL, OFP10_VERSION, 0);
+ nfmc = ofpbuf_put_uninit(msg, sizeof *nfmc);
+ nfmc->id = htonl(id);
+ return msg;
+}
+
+void
+ofputil_start_flow_update(struct list *replies)
+{
+ struct ofpbuf *msg;
+
+ msg = ofpraw_alloc_xid(OFPRAW_NXST_FLOW_MONITOR_REPLY, OFP10_VERSION,
+ htonl(0), 1024);
+
+ 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, 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->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);
+
+ ofpmp_postappend(replies, start_ofs);
+}
+\f
+struct ofpbuf *
+ofputil_encode_packet_out(const struct ofputil_packet_out *po,
+ enum ofputil_protocol protocol)
+{
+ enum ofp_version ofp_version = ofputil_protocol_to_ofp_version(protocol);
+ struct ofpbuf *msg;
+ size_t size;
+
+ size = po->ofpacts_len;
+ if (po->buffer_id == UINT32_MAX) {
+ size += po->packet_len;
+ }
+
+ switch (ofp_version) {
+ case OFP10_VERSION: {
+ struct ofp_packet_out *opo;
+ size_t actions_ofs;
+
+ msg = ofpraw_alloc(OFPRAW_OFPT10_PACKET_OUT, OFP10_VERSION, size);
+ ofpbuf_put_zeros(msg, sizeof *opo);
+ actions_ofs = msg->size;
+ ofpacts_put_openflow10(po->ofpacts, po->ofpacts_len, msg);
+
+ opo = msg->l3;
+ opo->buffer_id = htonl(po->buffer_id);
+ opo->in_port = htons(po->in_port);
+ opo->actions_len = htons(msg->size - actions_ofs);
+ break;
+ }
+
+ case OFP11_VERSION:
+ case OFP12_VERSION: {
+ struct ofp11_packet_out *opo;
+ size_t len;
+
+ msg = ofpraw_alloc(OFPRAW_OFPT11_PACKET_OUT, ofp_version, size);
+ ofpbuf_put_zeros(msg, sizeof *opo);
+ len = ofpacts_put_openflow11_actions(po->ofpacts, po->ofpacts_len, msg);
+
+ opo = msg->l3;
+ opo->buffer_id = htonl(po->buffer_id);
+ opo->in_port = ofputil_port_to_ofp11(po->in_port);
+ opo->actions_len = htons(len);
+ break;
+ }
+
+ default:
+ NOT_REACHED();
+ }
+
+ if (po->buffer_id == UINT32_MAX) {
+ ofpbuf_put(msg, po->packet, po->packet_len);
+ }
+
+ ofpmsg_update_length(msg);
+
+ return msg;
+}
+\f
+/* Creates and returns an OFPT_ECHO_REQUEST message with an empty payload. */
+struct ofpbuf *
+make_echo_request(enum ofp_version ofp_version)
+{
+ return ofpraw_alloc_xid(OFPRAW_OFPT_ECHO_REQUEST, ofp_version,
+ htonl(0), 0);
+}
+
+/* Creates and returns an OFPT_ECHO_REPLY message matching the
+ * OFPT_ECHO_REQUEST message in 'rq'. */
+struct ofpbuf *
+make_echo_reply(const struct ofp_header *rq)
+{
+ struct ofpbuf rq_buf;
+ struct ofpbuf *reply;
+
+ ofpbuf_use_const(&rq_buf, rq, ntohs(rq->length));
+ ofpraw_pull_assert(&rq_buf);
+
+ reply = ofpraw_alloc_reply(OFPRAW_OFPT_ECHO_REPLY, rq, rq_buf.size);
+ ofpbuf_put(reply, rq_buf.data, rq_buf.size);
+ return reply;
+}
+
+struct ofpbuf *
+ofputil_encode_barrier_request(enum ofp_version ofp_version)
+{
+ enum ofpraw type;
+
+ switch (ofp_version) {
+ case OFP12_VERSION:
+ case OFP11_VERSION:
+ type = OFPRAW_OFPT11_BARRIER_REQUEST;
+ break;
+
+ case OFP10_VERSION:
+ type = OFPRAW_OFPT10_BARRIER_REQUEST;
+ break;
+
+ default:
+ NOT_REACHED();
+ }
+
+ return ofpraw_alloc(type, ofp_version, 0);
+}
+
+const char *
+ofputil_frag_handling_to_string(enum ofp_config_flags flags)
+{
+ switch (flags & OFPC_FRAG_MASK) {
+ case OFPC_FRAG_NORMAL: return "normal";
+ case OFPC_FRAG_DROP: return "drop";
+ case OFPC_FRAG_REASM: return "reassemble";
+ case OFPC_FRAG_NX_MATCH: return "nx-match";
+ }
+
+ NOT_REACHED();
+}
+
+bool
+ofputil_frag_handling_from_string(const char *s, enum ofp_config_flags *flags)
+{
+ if (!strcasecmp(s, "normal")) {
+ *flags = OFPC_FRAG_NORMAL;
+ } else if (!strcasecmp(s, "drop")) {
+ *flags = OFPC_FRAG_DROP;
+ } else if (!strcasecmp(s, "reassemble")) {
+ *flags = OFPC_FRAG_REASM;
+ } else if (!strcasecmp(s, "nx-match")) {
+ *flags = OFPC_FRAG_NX_MATCH;
+ } else {
+ return false;
+ }
+ return true;
+}
+
+/* Converts the OpenFlow 1.1+ port number 'ofp11_port' into an OpenFlow 1.0
+ * port number and stores the latter in '*ofp10_port', for the purpose of
+ * decoding OpenFlow 1.1+ protocol messages. Returns 0 if successful,
+ * otherwise an OFPERR_* number.
+ *
+ * See the definition of OFP11_MAX for an explanation of the mapping. */
+enum ofperr
+ofputil_port_from_ofp11(ovs_be32 ofp11_port, uint16_t *ofp10_port)
+{
+ uint32_t ofp11_port_h = ntohl(ofp11_port);
+
+ if (ofp11_port_h < OFPP_MAX) {
+ *ofp10_port = ofp11_port_h;
+ return 0;
+ } else if (ofp11_port_h >= OFPP11_MAX) {
+ *ofp10_port = ofp11_port_h - OFPP11_OFFSET;
+ return 0;
+ } else {
+ VLOG_WARN_RL(&bad_ofmsg_rl, "port %"PRIu32" is outside the supported "
+ "range 0 through %d or 0x%"PRIx32" through 0x%"PRIx32,
+ ofp11_port_h, OFPP_MAX - 1,
+ (uint32_t) OFPP11_MAX, UINT32_MAX);
+ return OFPERR_OFPBAC_BAD_OUT_PORT;
+ }
+}
+
+/* Returns the OpenFlow 1.1+ port number equivalent to the OpenFlow 1.0 port
+ * number 'ofp10_port', for encoding OpenFlow 1.1+ protocol messages.
+ *
+ * See the definition of OFP11_MAX for an explanation of the mapping. */
+ovs_be32
+ofputil_port_to_ofp11(uint16_t ofp10_port)
+{
+ return htonl(ofp10_port < OFPP_MAX
+ ? ofp10_port
+ : ofp10_port + OFPP11_OFFSET);
+}
+
+/* Checks that 'port' is a valid output port for the OFPAT10_OUTPUT action, given
+ * that the switch will never have more than 'max_ports' ports. Returns 0 if
+ * 'port' is valid, otherwise an OpenFlow return code. */
+enum ofperr
+ofputil_check_output_port(uint16_t port, int max_ports)
+{
+ switch (port) {
+ case OFPP_IN_PORT:
+ case OFPP_TABLE:
+ case OFPP_NORMAL:
+ case OFPP_FLOOD:
+ case OFPP_ALL:
+ case OFPP_CONTROLLER:
+ case OFPP_NONE:
+ case OFPP_LOCAL:
+ return 0;
+
+ default:
+ if (port < max_ports) {
+ return 0;
+ }
+ return OFPERR_OFPBAC_BAD_OUT_PORT;
+ }
+}
+
+#define OFPUTIL_NAMED_PORTS \
+ OFPUTIL_NAMED_PORT(IN_PORT) \
+ OFPUTIL_NAMED_PORT(TABLE) \
+ OFPUTIL_NAMED_PORT(NORMAL) \
+ OFPUTIL_NAMED_PORT(FLOOD) \
+ OFPUTIL_NAMED_PORT(ALL) \
+ OFPUTIL_NAMED_PORT(CONTROLLER) \
+ OFPUTIL_NAMED_PORT(LOCAL) \
+ OFPUTIL_NAMED_PORT(NONE)
+
+/* Returns the port number represented by 's', which may be an integer or, for
+ * reserved ports, the standard OpenFlow name for the port (e.g. "LOCAL").
+ *
+ * Returns 0 if 's' is not a valid OpenFlow port number or name. The caller
+ * should issue an error message in this case, because this function usually
+ * does not. (This gives the caller an opportunity to look up the port name
+ * another way, e.g. by contacting the switch and listing the names of all its
+ * ports).
+ *
+ * This function accepts OpenFlow 1.0 port numbers. It also accepts a subset
+ * of OpenFlow 1.1+ port numbers, mapping those port numbers into the 16-bit
+ * range as described in include/openflow/openflow-1.1.h. */
+uint16_t
+ofputil_port_from_string(const char *s)
+{
+ unsigned int port32;
+
+ if (str_to_uint(s, 10, &port32)) {
+ if (port32 == 0) {
+ VLOG_WARN("port 0 is not a valid OpenFlow port number");
+ return 0;
+ } else if (port32 < OFPP_MAX) {
+ return port32;
+ } else if (port32 < OFPP_FIRST_RESV) {
+ VLOG_WARN("port %u is a reserved OF1.0 port number that will "
+ "be translated to %u when talking to an OF1.1 or "
+ "later controller", port32, port32 + OFPP11_OFFSET);
+ return port32;
+ } else if (port32 <= OFPP_LAST_RESV) {
+ struct ds s;
+
+ ds_init(&s);
+ ofputil_format_port(port32, &s);
+ VLOG_WARN_ONCE("referring to port %s as %u is deprecated for "
+ "compatibility with future versions of OpenFlow",
+ ds_cstr(&s), port32);
+ ds_destroy(&s);
+
+ return port32;
+ } else if (port32 < OFPP11_MAX) {
+ VLOG_WARN("port %u is outside the supported range 0 through "
+ "%"PRIx16"or 0x%x through 0x%"PRIx32, port32,
+ UINT16_MAX, (unsigned int) OFPP11_MAX, UINT32_MAX);
+ return 0;
+ } else {
+ return port32 - OFPP11_OFFSET;
+ }
+ } else {
+ struct pair {
+ const char *name;
+ uint16_t value;
+ };
+ static const struct pair pairs[] = {
+#define OFPUTIL_NAMED_PORT(NAME) {#NAME, OFPP_##NAME},
+ OFPUTIL_NAMED_PORTS
+#undef OFPUTIL_NAMED_PORT
+ };
+ const struct pair *p;
+
+ for (p = pairs; p < &pairs[ARRAY_SIZE(pairs)]; p++) {
+ if (!strcasecmp(s, p->name)) {
+ return p->value;
+ }
+ }
+ return 0;
+ }
+}
+
+/* Appends to 's' a string representation of the OpenFlow port number 'port'.
+ * Most ports' string representation is just the port number, but for special
+ * ports, e.g. OFPP_LOCAL, it is the name, e.g. "LOCAL". */
+void
+ofputil_format_port(uint16_t port, struct ds *s)
+{
+ const char *name;
+
+ switch (port) {
+#define OFPUTIL_NAMED_PORT(NAME) case OFPP_##NAME: name = #NAME; break;
+ OFPUTIL_NAMED_PORTS
+#undef OFPUTIL_NAMED_PORT
+
+ default:
+ ds_put_format(s, "%"PRIu16, port);
+ return;
+ }
+ ds_put_cstr(s, name);
+}
+
+/* Given a buffer 'b' that contains an array of OpenFlow ports of type
+ * 'ofp_version', tries to pull the first element from the array. If
+ * successful, initializes '*pp' with an abstract representation of the
+ * port and returns 0. If no ports remain to be decoded, returns EOF.
+ * On an error, returns a positive OFPERR_* value. */
+int
+ofputil_pull_phy_port(enum ofp_version ofp_version, struct ofpbuf *b,
+ struct ofputil_phy_port *pp)
+{
+ switch (ofp_version) {
+ case OFP10_VERSION: {
+ const struct ofp10_phy_port *opp = ofpbuf_try_pull(b, sizeof *opp);
+ return opp ? ofputil_decode_ofp10_phy_port(pp, opp) : EOF;
+ }
+ case OFP11_VERSION:
+ case OFP12_VERSION: {
+ const struct ofp11_port *op = ofpbuf_try_pull(b, sizeof *op);
+ return op ? ofputil_decode_ofp11_port(pp, op) : EOF;
+ }
+ default:
+ NOT_REACHED();
+ }
+}
+
+/* Given a buffer 'b' that contains an array of OpenFlow ports of type
+ * 'ofp_version', returns the number of elements. */
+size_t ofputil_count_phy_ports(uint8_t ofp_version, struct ofpbuf *b)
+{
+ return b->size / ofputil_get_phy_port_size(ofp_version);
+}
+
+/* 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.
+ *
+ * ofp-util.def lists the mapping from names to action. */
+int
+ofputil_action_code_from_name(const char *name)
+{
+ static const char *names[OFPUTIL_N_ACTIONS] = {
+ NULL,
+#define OFPAT10_ACTION(ENUM, STRUCT, NAME) NAME,
+#define OFPAT11_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME) NAME,
+#define NXAST_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME) NAME,
+#include "ofp-util.def"
+ };
+
+ const char **p;
+
+ for (p = names; p < &names[ARRAY_SIZE(names)]; p++) {
+ if (*p && !strcasecmp(name, *p)) {
+ return p - names;
+ }
+ }
+ return -1;
+}
+
+/* Appends an action of the type specified by 'code' to 'buf' and returns the
+ * action. Initializes the parts of 'action' that identify it as having type
+ * <ENUM> and length 'sizeof *action' and zeros the rest. For actions that
+ * have variable length, the length used and cleared is that of struct
+ * <STRUCT>. */
+void *
+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(ENUM, STRUCT, EXTENSIBLE, NAME) \
+ case OFPUTIL_##ENUM: return ofputil_put_##ENUM(buf);
+#define NXAST_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME) \
+ case OFPUTIL_##ENUM: return ofputil_put_##ENUM(buf);
+#include "ofp-util.def"
+ }
+ NOT_REACHED();
+}
+
+#define OFPAT10_ACTION(ENUM, STRUCT, NAME) \
+ void \
+ ofputil_init_##ENUM(struct STRUCT *s) \
+ { \
+ memset(s, 0, sizeof *s); \
+ s->type = htons(ENUM); \
+ s->len = htons(sizeof *s); \
+ } \
+ \
+ struct STRUCT * \
+ ofputil_put_##ENUM(struct ofpbuf *buf) \
+ { \
+ struct STRUCT *s = ofpbuf_put_uninit(buf, sizeof *s); \
+ ofputil_init_##ENUM(s); \
+ return s; \
+ }
+#define OFPAT11_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME) \
+ OFPAT10_ACTION(ENUM, STRUCT, NAME)
+#define NXAST_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME) \
+ void \
+ ofputil_init_##ENUM(struct STRUCT *s) \
+ { \
+ memset(s, 0, sizeof *s); \
+ s->type = htons(OFPAT10_VENDOR); \
+ s->len = htons(sizeof *s); \
+ s->vendor = htonl(NX_VENDOR_ID); \
+ s->subtype = htons(ENUM); \
+ } \
+ \
+ struct STRUCT * \
+ ofputil_put_##ENUM(struct ofpbuf *buf) \
+ { \
+ struct STRUCT *s = ofpbuf_put_uninit(buf, sizeof *s); \
+ ofputil_init_##ENUM(s); \
+ return s; \
+ }
+#include "ofp-util.def"
+
+static void
+ofputil_normalize_match__(struct match *match, bool may_log)
+{
+ enum {
+ MAY_NW_ADDR = 1 << 0, /* nw_src, nw_dst */
+ MAY_TP_ADDR = 1 << 1, /* tp_src, tp_dst */
+ MAY_NW_PROTO = 1 << 2, /* nw_proto */
+ MAY_IPVx = 1 << 3, /* tos, frag, ttl */
+ MAY_ARP_SHA = 1 << 4, /* arp_sha */
+ MAY_ARP_THA = 1 << 5, /* arp_tha */
+ MAY_IPV6 = 1 << 6, /* ipv6_src, ipv6_dst, ipv6_label */
+ MAY_ND_TARGET = 1 << 7 /* nd_target */
+ } may_match;
+
+ struct flow_wildcards wc;
+
+ /* Figure out what fields may be matched. */
+ if (match->flow.dl_type == htons(ETH_TYPE_IP)) {
+ may_match = MAY_NW_PROTO | MAY_IPVx | MAY_NW_ADDR;
+ if (match->flow.nw_proto == IPPROTO_TCP ||
+ match->flow.nw_proto == IPPROTO_UDP ||
+ match->flow.nw_proto == IPPROTO_ICMP) {
+ may_match |= MAY_TP_ADDR;
+ }
+ } else if (match->flow.dl_type == htons(ETH_TYPE_IPV6)) {
+ may_match = MAY_NW_PROTO | MAY_IPVx | MAY_IPV6;
+ if (match->flow.nw_proto == IPPROTO_TCP ||
+ match->flow.nw_proto == IPPROTO_UDP) {
+ may_match |= MAY_TP_ADDR;
+ } else if (match->flow.nw_proto == IPPROTO_ICMPV6) {
+ may_match |= MAY_TP_ADDR;
+ if (match->flow.tp_src == htons(ND_NEIGHBOR_SOLICIT)) {
+ may_match |= MAY_ND_TARGET | MAY_ARP_SHA;
+ } else if (match->flow.tp_src == htons(ND_NEIGHBOR_ADVERT)) {
+ may_match |= MAY_ND_TARGET | MAY_ARP_THA;
+ }
+ }
+ } else if (match->flow.dl_type == htons(ETH_TYPE_ARP)) {
+ may_match = MAY_NW_PROTO | MAY_NW_ADDR | MAY_ARP_SHA | MAY_ARP_THA;
+ } else {
+ may_match = 0;
+ }
+
+ /* Clear the fields that may not be matched. */
+ wc = match->wc;
+ if (!(may_match & MAY_NW_ADDR)) {
+ wc.masks.nw_src = wc.masks.nw_dst = htonl(0);
+ }
+ if (!(may_match & MAY_TP_ADDR)) {
+ wc.masks.tp_src = wc.masks.tp_dst = htons(0);
+ }
+ if (!(may_match & MAY_NW_PROTO)) {
+ wc.masks.nw_proto = 0;
+ }
+ if (!(may_match & MAY_IPVx)) {
+ wc.masks.nw_tos = 0;
+ wc.masks.nw_ttl = 0;
+ }
+ if (!(may_match & MAY_ARP_SHA)) {
+ memset(wc.masks.arp_sha, 0, ETH_ADDR_LEN);
+ }
+ if (!(may_match & MAY_ARP_THA)) {
+ memset(wc.masks.arp_tha, 0, ETH_ADDR_LEN);
+ }
+ if (!(may_match & MAY_IPV6)) {
+ wc.masks.ipv6_src = wc.masks.ipv6_dst = in6addr_any;
+ wc.masks.ipv6_label = htonl(0);
+ }
+ if (!(may_match & MAY_ND_TARGET)) {
+ wc.masks.nd_target = in6addr_any;
+ }
+
+ /* Log any changes. */
+ if (!flow_wildcards_equal(&wc, &match->wc)) {
+ bool log = may_log && !VLOG_DROP_INFO(&bad_ofmsg_rl);
+ char *pre = log ? match_to_string(match, OFP_DEFAULT_PRIORITY) : NULL;
+
+ match->wc = wc;
+ match_zero_wildcarded_fields(match);
+
+ if (log) {
+ char *post = match_to_string(match, OFP_DEFAULT_PRIORITY);
+ VLOG_INFO("normalization changed ofp_match, details:");
+ VLOG_INFO(" pre: %s", pre);
+ VLOG_INFO("post: %s", post);
+ free(pre);
+ free(post);
+ }
+ }
+}
+
+/* "Normalizes" the wildcards in 'match'. That means:
+ *
+ * 1. If the type of level N is known, then only the valid fields for that
+ * level may be specified. For example, ARP does not have a TOS field,
+ * so nw_tos must be wildcarded if 'match' specifies an ARP flow.
+ * Similarly, IPv4 does not have any IPv6 addresses, so ipv6_src and
+ * ipv6_dst (and other fields) must be wildcarded if 'match' specifies an
+ * IPv4 flow.
+ *
+ * 2. If the type of level N is not known (or not understood by Open
+ * vSwitch), then no fields at all for that level may be specified. For
+ * example, Open vSwitch does not understand SCTP, an L4 protocol, so the
+ * L4 fields tp_src and tp_dst must be wildcarded if 'match' specifies an
+ * SCTP flow.
+ *
+ * If this function changes 'match', it logs a rate-limited informational
+ * message. */
+void
+ofputil_normalize_match(struct match *match)
+{
+ ofputil_normalize_match__(match, true);
+}
+
+/* Same as ofputil_normalize_match() without the logging. Thus, this function
+ * is suitable for a program's internal use, whereas ofputil_normalize_match()
+ * sense for use on flows received from elsewhere (so that a bug in the program
+ * that sent them can be reported and corrected). */
+void
+ofputil_normalize_match_quiet(struct match *match)
+{
+ ofputil_normalize_match__(match, false);
+}
+
+/* Parses a key or a key-value pair from '*stringp'.
+ *
+ * On success: Stores the key into '*keyp'. Stores the value, if present, into
+ * '*valuep', otherwise an empty string. Advances '*stringp' past the end of
+ * the key-value pair, preparing it for another call. '*keyp' and '*valuep'
+ * are substrings of '*stringp' created by replacing some of its bytes by null
+ * terminators. Returns true.
+ *
+ * If '*stringp' is just white space or commas, sets '*keyp' and '*valuep' to
+ * NULL and returns false. */
+bool
+ofputil_parse_key_value(char **stringp, char **keyp, char **valuep)
+{
+ char *pos, *key, *value;
+ size_t key_len;
+
+ pos = *stringp;
+ pos += strspn(pos, ", \t\r\n");
+ if (*pos == '\0') {
+ *keyp = *valuep = NULL;
+ return false;
+ }
+
+ key = pos;
+ key_len = strcspn(pos, ":=(, \t\r\n");
+ if (key[key_len] == ':' || key[key_len] == '=') {
+ /* The value can be separated by a colon. */
+ size_t value_len;
+
+ value = key + key_len + 1;
+ value_len = strcspn(value, ", \t\r\n");
+ pos = value + value_len + (value[value_len] != '\0');
+ value[value_len] = '\0';
+ } else if (key[key_len] == '(') {
+ /* The value can be surrounded by balanced parentheses. The outermost
+ * set of parentheses is removed. */
+ int level = 1;
+ size_t value_len;
+
+ value = key + key_len + 1;
+ for (value_len = 0; level > 0; value_len++) {
+ switch (value[value_len]) {
+ case '\0':
+ level = 0;
+ break;
+
+ case '(':
+ level++;
+ break;
+
+ case ')':
+ level--;
+ break;
+ }
+ }
+ value[value_len - 1] = '\0';
+ pos = value + value_len;
+ } else {
+ /* There might be no value at all. */
+ value = key + key_len; /* Will become the empty string below. */
+ pos = key + key_len + (key[key_len] != '\0');
+ }
+ key[key_len] = '\0';
+
+ *stringp = pos;
+ *keyp = key;
+ *valuep = value;
+ return true;
+}
+
+/* Encode a dump ports request for 'port', the encoded message
+ * will be fore Open Flow version 'ofp_version'. Returns message
+ * as a struct ofpbuf. Returns encoded message on success, NULL on error */
+struct ofpbuf *
+ofputil_encode_dump_ports_request(enum ofp_version ofp_version, int16_t port)
+{
+ struct ofpbuf *request;
+
+ switch (ofp_version) {
+ case OFP10_VERSION: {
+ struct ofp10_port_stats_request *req;
+ request = ofpraw_alloc(OFPRAW_OFPST10_PORT_REQUEST, ofp_version, 0);
+ req = ofpbuf_put_zeros(request, sizeof *req);
+ req->port_no = htons(port);
+ break;
+ }
+ case OFP11_VERSION:
+ case OFP12_VERSION: {
+ struct ofp11_port_stats_request *req;
+ request = ofpraw_alloc(OFPRAW_OFPST11_PORT_REQUEST, ofp_version, 0);
+ req = ofpbuf_put_zeros(request, sizeof *req);
+ req->port_no = ofputil_port_to_ofp11(port);
+ break;
+ }
+ default:
+ NOT_REACHED();
+ }
+
+ return request;
+}
+
+static void
+ofputil_port_stats_to_ofp10(const struct ofputil_port_stats *ops,
+ struct ofp10_port_stats *ps10)
+{
+ ps10->port_no = htons(ops->port_no);
+ memset(ps10->pad, 0, sizeof ps10->pad);
+ put_32aligned_be64(&ps10->rx_packets, htonll(ops->stats.rx_packets));
+ put_32aligned_be64(&ps10->tx_packets, htonll(ops->stats.tx_packets));
+ put_32aligned_be64(&ps10->rx_bytes, htonll(ops->stats.rx_bytes));
+ put_32aligned_be64(&ps10->tx_bytes, htonll(ops->stats.tx_bytes));
+ put_32aligned_be64(&ps10->rx_dropped, htonll(ops->stats.rx_dropped));
+ put_32aligned_be64(&ps10->tx_dropped, htonll(ops->stats.tx_dropped));
+ put_32aligned_be64(&ps10->rx_errors, htonll(ops->stats.rx_errors));
+ put_32aligned_be64(&ps10->tx_errors, htonll(ops->stats.tx_errors));
+ put_32aligned_be64(&ps10->rx_frame_err, htonll(ops->stats.rx_frame_errors));
+ put_32aligned_be64(&ps10->rx_over_err, htonll(ops->stats.rx_over_errors));
+ put_32aligned_be64(&ps10->rx_crc_err, htonll(ops->stats.rx_crc_errors));
+ put_32aligned_be64(&ps10->collisions, htonll(ops->stats.collisions));
+}
+
+static void
+ofputil_port_stats_to_ofp11(const struct ofputil_port_stats *ops,
+ struct ofp11_port_stats *ps11)
+{
+ ps11->port_no = ofputil_port_to_ofp11(ops->port_no);
+ memset(ps11->pad, 0, sizeof ps11->pad);
+ ps11->rx_packets = htonll(ops->stats.rx_packets);
+ ps11->tx_packets = htonll(ops->stats.tx_packets);
+ ps11->rx_bytes = htonll(ops->stats.rx_bytes);
+ ps11->tx_bytes = htonll(ops->stats.tx_bytes);
+ ps11->rx_dropped = htonll(ops->stats.rx_dropped);
+ ps11->tx_dropped = htonll(ops->stats.tx_dropped);
+ ps11->rx_errors = htonll(ops->stats.rx_errors);
+ ps11->tx_errors = htonll(ops->stats.tx_errors);
+ ps11->rx_frame_err = htonll(ops->stats.rx_frame_errors);
+ ps11->rx_over_err = htonll(ops->stats.rx_over_errors);
+ ps11->rx_crc_err = htonll(ops->stats.rx_crc_errors);
+ ps11->collisions = htonll(ops->stats.collisions);
+}
+
+/* Encode a ports stat for 'ops' and append it to 'replies'. */
+void
+ofputil_append_port_stat(struct list *replies,
+ const struct ofputil_port_stats *ops)
+{
+ struct ofpbuf *msg = ofpbuf_from_list(list_back(replies));
+ struct ofp_header *oh = msg->data;
+
+ switch ((enum ofp_version)oh->version) {
+ case OFP12_VERSION:
+ case OFP11_VERSION: {
+ struct ofp11_port_stats *reply = ofpmp_append(replies, sizeof *reply);
+ ofputil_port_stats_to_ofp11(ops, reply);
+ break;
+ }
+
+ case OFP10_VERSION: {
+ struct ofp10_port_stats *reply = ofpmp_append(replies, sizeof *reply);
+ ofputil_port_stats_to_ofp10(ops, reply);
+ break;
+ }
+
+ default:
+ NOT_REACHED();
+ }
+}
+
+static enum ofperr
+ofputil_port_stats_from_ofp10(struct ofputil_port_stats *ops,
+ const struct ofp10_port_stats *ps10)
+{
+ memset(ops, 0, sizeof *ops);
+
+ ops->port_no = ntohs(ps10->port_no);
+ ops->stats.rx_packets = ntohll(get_32aligned_be64(&ps10->rx_packets));
+ ops->stats.tx_packets = ntohll(get_32aligned_be64(&ps10->tx_packets));
+ ops->stats.rx_bytes = ntohll(get_32aligned_be64(&ps10->rx_bytes));
+ ops->stats.tx_bytes = ntohll(get_32aligned_be64(&ps10->tx_bytes));
+ ops->stats.rx_dropped = ntohll(get_32aligned_be64(&ps10->rx_dropped));
+ ops->stats.tx_dropped = ntohll(get_32aligned_be64(&ps10->tx_dropped));
+ ops->stats.rx_errors = ntohll(get_32aligned_be64(&ps10->rx_errors));
+ ops->stats.tx_errors = ntohll(get_32aligned_be64(&ps10->tx_errors));
+ ops->stats.rx_frame_errors =
+ ntohll(get_32aligned_be64(&ps10->rx_frame_err));
+ ops->stats.rx_over_errors = ntohll(get_32aligned_be64(&ps10->rx_over_err));
+ ops->stats.rx_crc_errors = ntohll(get_32aligned_be64(&ps10->rx_crc_err));
+ ops->stats.collisions = ntohll(get_32aligned_be64(&ps10->collisions));
+
+ return 0;
+}
+
+static enum ofperr
+ofputil_port_stats_from_ofp11(struct ofputil_port_stats *ops,
+ const struct ofp11_port_stats *ps11)
+{
+ enum ofperr error;
+
+ memset(ops, 0, sizeof *ops);
+ error = ofputil_port_from_ofp11(ps11->port_no, &ops->port_no);
+ if (error) {
+ return error;
+ }
+
+ ops->stats.rx_packets = ntohll(ps11->rx_packets);
+ ops->stats.tx_packets = ntohll(ps11->tx_packets);
+ ops->stats.rx_bytes = ntohll(ps11->rx_bytes);
+ ops->stats.tx_bytes = ntohll(ps11->tx_bytes);
+ ops->stats.rx_dropped = ntohll(ps11->rx_dropped);
+ ops->stats.tx_dropped = ntohll(ps11->tx_dropped);
+ ops->stats.rx_errors = ntohll(ps11->rx_errors);
+ ops->stats.tx_errors = ntohll(ps11->tx_errors);
+ ops->stats.rx_frame_errors = ntohll(ps11->rx_frame_err);
+ ops->stats.rx_over_errors = ntohll(ps11->rx_over_err);
+ ops->stats.rx_crc_errors = ntohll(ps11->rx_crc_err);
+ ops->stats.collisions = ntohll(ps11->collisions);
+
+ return 0;
+}
+
+/* Returns the number of port stats elements in OFPTYPE_PORT_STATS_REPLY
+ * message 'oh'. */
+size_t
+ofputil_count_port_stats(const struct ofp_header *oh)
+{
+ struct ofpbuf b;
+
+ ofpbuf_use_const(&b, oh, ntohs(oh->length));
+ ofpraw_pull_assert(&b);
+
+ BUILD_ASSERT(sizeof(struct ofp10_port_stats) ==
+ sizeof(struct ofp11_port_stats));
+ return b.size / sizeof(struct ofp10_port_stats);
+}
+
+/* Converts an OFPST_PORT_STATS reply in 'msg' into an abstract
+ * ofputil_port_stats in 'ps'.
+ *
+ * Multiple OFPST_PORT_STATS 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.
+ *
+ * Returns 0 if successful, EOF if no replies were left in this 'msg',
+ * otherwise a positive errno value. */
+int
+ofputil_decode_port_stats(struct ofputil_port_stats *ps, struct ofpbuf *msg)
+{
+ enum ofperr error;
+ enum ofpraw raw;
+
+ error = (msg->l2
+ ? ofpraw_decode(&raw, msg->l2)
+ : ofpraw_pull(&raw, msg));
+ if (error) {
+ return error;
+ }
+
+ if (!msg->size) {
+ return EOF;
+ } else if (raw == OFPRAW_OFPST11_PORT_REPLY) {
+ const struct ofp11_port_stats *ps11;
+
+ ps11 = ofpbuf_try_pull(msg, sizeof *ps11);
+ if (!ps11) {
+ VLOG_WARN_RL(&bad_ofmsg_rl, "OFPST_PORT reply has %zu leftover "
+ "bytes at end", msg->size);
+ return OFPERR_OFPBRC_BAD_LEN;
+ }
+ return ofputil_port_stats_from_ofp11(ps, ps11);
+ } else if (raw == OFPRAW_OFPST10_PORT_REPLY) {
+ const struct ofp10_port_stats *ps10;
+
+ ps10 = ofpbuf_try_pull(msg, sizeof *ps10);
+ if (!ps10) {
+ VLOG_WARN_RL(&bad_ofmsg_rl, "OFPST_PORT reply has %zu leftover "
+ "bytes at end", msg->size);
+ return OFPERR_OFPBRC_BAD_LEN;
+ }
+ return ofputil_port_stats_from_ofp10(ps, ps10);
+ } else {
+ NOT_REACHED();
+ }
+
+}
+
+/* Parse a port status request message into a 16 bit OpenFlow 1.0
+ * port number and stores the latter in '*ofp10_port'.
+ * Returns 0 if successful, otherwise an OFPERR_* number. */
+enum ofperr
+ofputil_decode_port_stats_request(const struct ofp_header *request,
+ uint16_t *ofp10_port)
+{
+ switch ((enum ofp_version)request->version) {
+ case OFP12_VERSION:
+ case OFP11_VERSION: {
+ const struct ofp11_port_stats_request *psr11 = ofpmsg_body(request);
+ return ofputil_port_from_ofp11(psr11->port_no, ofp10_port);
+ }
+
+ case OFP10_VERSION: {
+ const struct ofp10_port_stats_request *psr10 = ofpmsg_body(request);
+ *ofp10_port = ntohs(psr10->port_no);
+ return 0;
+ }
+
+ default:
+ NOT_REACHED();
+ }
+}
+
+/* Parse a queue status request message into 'oqsr'.
+ * Returns 0 if successful, otherwise an OFPERR_* number. */
+enum ofperr
+ofputil_decode_queue_stats_request(const struct ofp_header *request,
+ struct ofputil_queue_stats_request *oqsr)
+{
+ switch ((enum ofp_version)request->version) {
+ case OFP12_VERSION:
+ case OFP11_VERSION: {
+ const struct ofp11_queue_stats_request *qsr11 = ofpmsg_body(request);
+ oqsr->queue_id = ntohl(qsr11->queue_id);
+ return ofputil_port_from_ofp11(qsr11->port_no, &oqsr->port_no);
+ }
+
+ case OFP10_VERSION: {
+ const struct ofp10_queue_stats_request *qsr11 = ofpmsg_body(request);
+ oqsr->queue_id = ntohl(qsr11->queue_id);
+ oqsr->port_no = ntohs(qsr11->port_no);
+ return 0;
+ }
+
+ default:
+ NOT_REACHED();
+ }
+}
+
+/* Encode a queue statsrequest for 'oqsr', the encoded message
+ * will be fore Open Flow version 'ofp_version'. Returns message
+ * as a struct ofpbuf. Returns encoded message on success, NULL on error */
+struct ofpbuf *
+ofputil_encode_queue_stats_request(enum ofp_version ofp_version,
+ const struct ofputil_queue_stats_request *oqsr)
+{
+ struct ofpbuf *request;
+
+ switch (ofp_version) {
+ case OFP11_VERSION:
+ case OFP12_VERSION: {
+ struct ofp11_queue_stats_request *req;
+ request = ofpraw_alloc(OFPRAW_OFPST11_QUEUE_REQUEST, ofp_version, 0);
+ req = ofpbuf_put_zeros(request, sizeof *req);
+ req->port_no = ofputil_port_to_ofp11(oqsr->port_no);
+ req->queue_id = htonl(oqsr->queue_id);
+ break;
+ }
+ case OFP10_VERSION: {
+ struct ofp10_queue_stats_request *req;
+ request = ofpraw_alloc(OFPRAW_OFPST10_QUEUE_REQUEST, ofp_version, 0);
+ req = ofpbuf_put_zeros(request, sizeof *req);
+ req->port_no = htons(oqsr->port_no);
+ req->queue_id = htonl(oqsr->queue_id);
+ break;
+ }
+ default:
+ NOT_REACHED();
+ }
+
+ return request;
+}
+
+/* Returns the number of queue stats elements in OFPTYPE_QUEUE_STATS_REPLY
+ * message 'oh'. */
+size_t
+ofputil_count_queue_stats(const struct ofp_header *oh)
+{
+ struct ofpbuf b;
+
+ ofpbuf_use_const(&b, oh, ntohs(oh->length));
+ ofpraw_pull_assert(&b);
+
+ BUILD_ASSERT(sizeof(struct ofp10_queue_stats) ==
+ sizeof(struct ofp11_queue_stats));
+ return b.size / sizeof(struct ofp10_queue_stats);
+}
+
+static enum ofperr
+ofputil_queue_stats_from_ofp10(struct ofputil_queue_stats *oqs,
+ const struct ofp10_queue_stats *qs10)
+{
+ oqs->port_no = ntohs(qs10->port_no);
+ oqs->queue_id = ntohl(qs10->queue_id);
+ oqs->stats.tx_bytes = ntohll(get_32aligned_be64(&qs10->tx_bytes));
+ oqs->stats.tx_packets = ntohll(get_32aligned_be64(&qs10->tx_packets));
+ oqs->stats.tx_errors = ntohll(get_32aligned_be64(&qs10->tx_errors));
+
+ return 0;
+}
+
+static enum ofperr
+ofputil_queue_stats_from_ofp11(struct ofputil_queue_stats *oqs,
+ const struct ofp11_queue_stats *qs11)
+{
+ enum ofperr error;
+
+ error = ofputil_port_from_ofp11(qs11->port_no, &oqs->port_no);
+ if (error) {
+ return error;