+ ovs_assert(!(protocols & ~OFPUTIL_P_ANY));
+ if (protocols == 0) {
+ return xstrdup("none");
+ }
+
+ ds_init(&s);
+ while (protocols) {
+ const struct proto_abbrev *p;
+ int i;
+
+ if (s.length) {
+ ds_put_char(&s, ',');
+ }
+
+ for (p = proto_abbrevs; p < &proto_abbrevs[N_PROTO_ABBREVS]; p++) {
+ if ((protocols & p->protocol) == p->protocol) {
+ ds_put_cstr(&s, p->name);
+ protocols &= ~p->protocol;
+ goto match;
+ }
+ }
+
+ for (i = 0; i < CHAR_BIT * sizeof(enum ofputil_protocol); i++) {
+ enum ofputil_protocol bit = 1u << i;
+
+ if (protocols & bit) {
+ ds_put_cstr(&s, ofputil_protocol_to_string(bit));
+ protocols &= ~bit;
+ goto match;
+ }
+ }
+ NOT_REACHED();
+
+ match: ;
+ }
+ return ds_steal_cstr(&s);
+}
+
+static enum ofputil_protocol
+ofputil_protocol_from_string__(const char *s, size_t n)
+{
+ const struct proto_abbrev *p;
+ int i;
+
+ for (i = 0; i < CHAR_BIT * sizeof(enum ofputil_protocol); i++) {
+ enum ofputil_protocol bit = 1u << i;
+ const char *name = ofputil_protocol_to_string(bit);
+
+ if (name && n == strlen(name) && !strncasecmp(s, name, n)) {
+ return bit;
+ }
+ }
+
+ for (p = proto_abbrevs; p < &proto_abbrevs[N_PROTO_ABBREVS]; p++) {
+ if (n == strlen(p->name) && !strncasecmp(s, p->name, n)) {
+ return p->protocol;
+ }
+ }
+
+ return 0;
+}
+
+/* Returns the nonempty set of protocols represented by 's', which can be a
+ * single protocol name or abbreviation or a comma-separated list of them.
+ *
+ * Aborts the program with an error message if 's' is invalid. */
+enum ofputil_protocol
+ofputil_protocols_from_string(const char *s)
+{
+ const char *orig_s = s;
+ enum ofputil_protocol protocols;
+
+ protocols = 0;
+ while (*s) {
+ enum ofputil_protocol p;
+ size_t n;
+
+ n = strcspn(s, ",");
+ if (n == 0) {
+ s++;
+ continue;
+ }
+
+ p = ofputil_protocol_from_string__(s, n);
+ if (!p) {
+ ovs_fatal(0, "%.*s: unknown flow protocol", (int) n, s);
+ }
+ protocols |= p;
+
+ s += n;
+ }
+
+ if (!protocols) {
+ ovs_fatal(0, "%s: no flow protocol specified", orig_s);
+ }
+ return protocols;
+}
+
+static int
+ofputil_version_from_string(const char *s)
+{
+ if (!strcasecmp(s, "OpenFlow10")) {
+ return OFP10_VERSION;
+ }
+ if (!strcasecmp(s, "OpenFlow11")) {
+ return OFP11_VERSION;
+ }
+ if (!strcasecmp(s, "OpenFlow12")) {
+ return OFP12_VERSION;
+ }
+ if (!strcasecmp(s, "OpenFlow13")) {
+ return OFP13_VERSION;
+ }
+ return 0;
+}
+
+static bool
+is_delimiter(unsigned char c)
+{
+ return isspace(c) || c == ',';
+}
+
+uint32_t
+ofputil_versions_from_string(const char *s)
+{
+ size_t i = 0;
+ uint32_t bitmap = 0;
+
+ while (s[i]) {
+ size_t j;
+ int version;
+ char *key;
+
+ if (is_delimiter(s[i])) {
+ i++;
+ continue;
+ }
+ j = 0;
+ while (s[i + j] && !is_delimiter(s[i + j])) {
+ j++;
+ }
+ key = xmemdup0(s + i, j);
+ version = ofputil_version_from_string(key);
+ if (!version) {
+ VLOG_FATAL("Unknown OpenFlow version: \"%s\"", key);
+ }
+ free(key);
+ bitmap |= 1u << version;
+ i += j;
+ }
+
+ return bitmap;
+}
+
+uint32_t
+ofputil_versions_from_strings(char ** const s, size_t count)
+{
+ uint32_t bitmap = 0;
+
+ while (count--) {
+ int version = ofputil_version_from_string(s[count]);
+ if (!version) {
+ VLOG_WARN("Unknown OpenFlow version: \"%s\"", s[count]);
+ } else {
+ bitmap |= 1u << version;
+ }
+ }
+
+ return bitmap;
+}
+
+const char *
+ofputil_version_to_string(enum ofp_version ofp_version)
+{
+ switch (ofp_version) {
+ case OFP10_VERSION:
+ return "OpenFlow10";
+ case OFP11_VERSION:
+ return "OpenFlow11";
+ case OFP12_VERSION:
+ return "OpenFlow12";
+ case OFP13_VERSION:
+ return "OpenFlow13";
+ default:
+ NOT_REACHED();
+ }
+}
+
+bool
+ofputil_packet_in_format_is_valid(enum nx_packet_in_format packet_in_format)
+{
+ switch (packet_in_format) {
+ case NXPIF_OPENFLOW10:
+ case NXPIF_NXM:
+ return true;
+ }
+
+ return false;
+}
+
+const char *
+ofputil_packet_in_format_to_string(enum nx_packet_in_format packet_in_format)
+{
+ switch (packet_in_format) {
+ case NXPIF_OPENFLOW10:
+ return "openflow10";
+ case NXPIF_NXM:
+ return "nxm";
+ default:
+ NOT_REACHED();
+ }
+}
+
+int
+ofputil_packet_in_format_from_string(const char *s)
+{
+ return (!strcmp(s, "openflow10") ? NXPIF_OPENFLOW10
+ : !strcmp(s, "nxm") ? NXPIF_NXM
+ : -1);
+}
+
+static bool
+regs_fully_wildcarded(const struct flow_wildcards *wc)
+{
+ int i;
+
+ for (i = 0; i < FLOW_N_REGS; i++) {
+ if (wc->masks.regs[i] != 0) {
+ return false;
+ }
+ }
+ return true;
+}
+
+/* Returns a bit-mask of ofputil_protocols that can be used for sending 'match'
+ * to a switch (e.g. to add or remove a flow). Only NXM can handle tunnel IDs,
+ * registers, or fixing the Ethernet multicast bit. Otherwise, it's better to
+ * use OpenFlow 1.0 protocol for backward compatibility. */
+enum ofputil_protocol
+ofputil_usable_protocols(const struct match *match)
+{
+ const struct flow_wildcards *wc = &match->wc;
+
+ BUILD_ASSERT_DECL(FLOW_WC_SEQ == 20);
+
+ /* These tunnel params can't be sent in a flow_mod */
+ if (wc->masks.tunnel.ip_ttl
+ || wc->masks.tunnel.ip_tos || wc->masks.tunnel.flags) {
+ return OFPUTIL_P_NONE;
+ }
+
+ /* skb_priority can't be sent in a flow_mod */
+ if (wc->masks.skb_priority) {
+ return OFPUTIL_P_NONE;
+ }
+
+ /* NXM and OXM support pkt_mark */
+ if (wc->masks.pkt_mark) {
+ return OFPUTIL_P_OF10_NXM_ANY | OFPUTIL_P_OF12_OXM
+ | OFPUTIL_P_OF13_OXM;
+ }
+
+ /* NXM, OXM, and OF1.1 support bitwise matching on ethernet addresses. */
+ if (!eth_mask_is_exact(wc->masks.dl_src)
+ && !eth_addr_is_zero(wc->masks.dl_src)) {
+ return OFPUTIL_P_OF10_NXM_ANY | OFPUTIL_P_OF12_OXM
+ | OFPUTIL_P_OF13_OXM;
+ }
+ if (!eth_mask_is_exact(wc->masks.dl_dst)
+ && !eth_addr_is_zero(wc->masks.dl_dst)) {
+ return OFPUTIL_P_OF10_NXM_ANY | OFPUTIL_P_OF12_OXM
+ | OFPUTIL_P_OF13_OXM;
+ }
+
+ /* NXM, OXM, and OF1.1+ support matching metadata. */
+ if (wc->masks.metadata != htonll(0)) {
+ return OFPUTIL_P_OF10_NXM_ANY | OFPUTIL_P_OF12_OXM
+ | OFPUTIL_P_OF13_OXM;
+ }
+
+ /* NXM and OXM support matching ARP hardware addresses. */
+ if (!eth_addr_is_zero(wc->masks.arp_sha) ||
+ !eth_addr_is_zero(wc->masks.arp_tha)) {
+ return OFPUTIL_P_OF10_NXM_ANY | OFPUTIL_P_OF12_OXM
+ | OFPUTIL_P_OF13_OXM;
+ }
+
+ /* NXM and OXM support matching L3 and L4 fields within IPv6.
+ *
+ * (arp_sha, arp_tha, nw_frag, and nw_ttl are covered elsewhere so they
+ * don't need to be included in this test too.) */
+ if (match->flow.dl_type == htons(ETH_TYPE_IPV6)
+ && (!ipv6_mask_is_any(&wc->masks.ipv6_src)
+ || !ipv6_mask_is_any(&wc->masks.ipv6_dst)
+ || !ipv6_mask_is_any(&wc->masks.nd_target)
+ || wc->masks.ipv6_label
+ || wc->masks.tp_src
+ || wc->masks.tp_dst
+ || wc->masks.nw_proto
+ || wc->masks.nw_tos)) {
+ return OFPUTIL_P_OF10_NXM_ANY | OFPUTIL_P_OF12_OXM
+ | OFPUTIL_P_OF13_OXM;
+ }
+
+ /* NXM and OXM support matching registers. */
+ if (!regs_fully_wildcarded(wc)) {
+ return OFPUTIL_P_OF10_NXM_ANY | OFPUTIL_P_OF12_OXM
+ | OFPUTIL_P_OF13_OXM;
+ }
+
+ /* NXM and OXM support matching tun_id, tun_src, and tun_dst. */
+ if (wc->masks.tunnel.tun_id != htonll(0)
+ || wc->masks.tunnel.ip_src != htonl(0)
+ || wc->masks.tunnel.ip_dst != htonl(0)) {
+ return OFPUTIL_P_OF10_NXM_ANY | OFPUTIL_P_OF12_OXM
+ | OFPUTIL_P_OF13_OXM;
+ }
+
+ /* NXM and OXM support matching fragments. */
+ if (wc->masks.nw_frag) {
+ return OFPUTIL_P_OF10_NXM_ANY | OFPUTIL_P_OF12_OXM
+ | OFPUTIL_P_OF13_OXM;
+ }
+
+ /* NXM and OXM support matching IP ECN bits. */
+ if (wc->masks.nw_tos & IP_ECN_MASK) {
+ return OFPUTIL_P_OF10_NXM_ANY | OFPUTIL_P_OF12_OXM
+ | OFPUTIL_P_OF13_OXM;
+ }
+
+ /* NXM and OXM support matching IP TTL/hop limit. */
+ if (wc->masks.nw_ttl) {
+ return OFPUTIL_P_OF10_NXM_ANY | OFPUTIL_P_OF12_OXM
+ | OFPUTIL_P_OF13_OXM;
+ }
+
+ /* NXM and OXM support non-CIDR IPv4 address masks. */
+ if (!ip_is_cidr(wc->masks.nw_src) || !ip_is_cidr(wc->masks.nw_dst)) {
+ return OFPUTIL_P_OF10_NXM_ANY | OFPUTIL_P_OF12_OXM
+ | OFPUTIL_P_OF13_OXM;
+ }
+
+ /* NXM and OXM support bitwise matching on transport port. */
+ if ((wc->masks.tp_src && wc->masks.tp_src != htons(UINT16_MAX)) ||
+ (wc->masks.tp_dst && wc->masks.tp_dst != htons(UINT16_MAX))) {
+ return OFPUTIL_P_OF10_NXM_ANY | OFPUTIL_P_OF12_OXM
+ | OFPUTIL_P_OF13_OXM;
+ }
+
+ /* NXM and OF1.1+ support matching MPLS label */
+ if (wc->masks.mpls_lse & htonl(MPLS_LABEL_MASK)) {
+ return OFPUTIL_P_OF10_NXM_ANY | OFPUTIL_P_OF12_OXM
+ | OFPUTIL_P_OF13_OXM;
+ }
+
+ /* NXM and OF1.1+ support matching MPLS TC */
+ if (wc->masks.mpls_lse & htonl(MPLS_TC_MASK)) {
+ return OFPUTIL_P_OF10_NXM_ANY | OFPUTIL_P_OF12_OXM
+ | OFPUTIL_P_OF13_OXM;
+ }
+
+ /* NXM and OF1.3+ support matching MPLS stack flag */
+ /* Allow for OF1.2 as there doesn't seem to be a
+ * particularly good reason not to */
+ if (wc->masks.mpls_lse & htonl(MPLS_BOS_MASK)) {
+ return OFPUTIL_P_OF10_NXM_ANY | OFPUTIL_P_OF12_OXM
+ | OFPUTIL_P_OF13_OXM;
+ }
+
+ /* Other formats can express this rule. */
+ return OFPUTIL_P_ANY;
+}
+
+void
+ofputil_format_version(struct ds *msg, enum ofp_version version)
+{
+ ds_put_format(msg, "0x%02x", version);
+}
+
+void
+ofputil_format_version_name(struct ds *msg, enum ofp_version version)
+{
+ ds_put_cstr(msg, ofputil_version_to_string(version));
+}
+
+static void
+ofputil_format_version_bitmap__(struct ds *msg, uint32_t bitmap,
+ void (*format_version)(struct ds *msg,
+ enum ofp_version))
+{
+ while (bitmap) {
+ format_version(msg, raw_ctz(bitmap));
+ bitmap = zero_rightmost_1bit(bitmap);
+ if (bitmap) {
+ ds_put_cstr(msg, ", ");
+ }
+ }
+}
+
+void
+ofputil_format_version_bitmap(struct ds *msg, uint32_t bitmap)
+{
+ ofputil_format_version_bitmap__(msg, bitmap, ofputil_format_version);
+}
+
+void
+ofputil_format_version_bitmap_names(struct ds *msg, uint32_t bitmap)
+{
+ ofputil_format_version_bitmap__(msg, bitmap, ofputil_format_version_name);
+}
+
+static bool
+ofputil_decode_hello_bitmap(const struct ofp_hello_elem_header *oheh,
+ uint32_t *allowed_versionsp)
+{
+ uint16_t bitmap_len = ntohs(oheh->length) - sizeof *oheh;
+ const ovs_be32 *bitmap = ALIGNED_CAST(const ovs_be32 *, oheh + 1);
+ uint32_t allowed_versions;
+
+ if (!bitmap_len || bitmap_len % sizeof *bitmap) {
+ return false;
+ }
+
+ /* Only use the first 32-bit element of the bitmap as that is all the
+ * current implementation supports. Subsequent elements are ignored which
+ * should have no effect on session negotiation until Open vSwtich supports
+ * wire-protocol versions greater than 31.
+ */
+ allowed_versions = ntohl(bitmap[0]);
+
+ if (allowed_versions & 1) {
+ /* There's no OpenFlow version 0. */
+ VLOG_WARN_RL(&bad_ofmsg_rl, "peer claims to support invalid OpenFlow "
+ "version 0x00");
+ allowed_versions &= ~1u;
+ }
+
+ if (!allowed_versions) {
+ VLOG_WARN_RL(&bad_ofmsg_rl, "peer does not support any OpenFlow "
+ "version (between 0x01 and 0x1f)");
+ return false;
+ }
+
+ *allowed_versionsp = allowed_versions;
+ return true;
+}
+
+static uint32_t
+version_bitmap_from_version(uint8_t ofp_version)
+{
+ return ((ofp_version < 32 ? 1u << ofp_version : 0) - 1) << 1;
+}
+
+/* Decodes OpenFlow OFPT_HELLO message 'oh', storing into '*allowed_versions'
+ * the set of OpenFlow versions for which 'oh' announces support.
+ *
+ * Because of how OpenFlow defines OFPT_HELLO messages, this function is always
+ * successful, and thus '*allowed_versions' is always initialized. However, it
+ * returns false if 'oh' contains some data that could not be fully understood,
+ * true if 'oh' was completely parsed. */
+bool
+ofputil_decode_hello(const struct ofp_header *oh, uint32_t *allowed_versions)
+{
+ struct ofpbuf msg;
+ bool ok = true;
+
+ ofpbuf_use_const(&msg, oh, ntohs(oh->length));
+ ofpbuf_pull(&msg, sizeof *oh);
+
+ *allowed_versions = version_bitmap_from_version(oh->version);
+ while (msg.size) {
+ const struct ofp_hello_elem_header *oheh;
+ unsigned int len;
+
+ if (msg.size < sizeof *oheh) {
+ return false;
+ }
+
+ oheh = msg.data;
+ len = ntohs(oheh->length);
+ if (len < sizeof *oheh || !ofpbuf_try_pull(&msg, ROUND_UP(len, 8))) {
+ return false;
+ }
+
+ if (oheh->type != htons(OFPHET_VERSIONBITMAP)
+ || !ofputil_decode_hello_bitmap(oheh, allowed_versions)) {
+ ok = false;
+ }
+ }
+
+ return ok;
+}
+
+/* Returns true if 'allowed_versions' needs to be accompanied by a version
+ * bitmap to be correctly expressed in an OFPT_HELLO message. */
+static bool
+should_send_version_bitmap(uint32_t allowed_versions)
+{
+ return !is_pow2((allowed_versions >> 1) + 1);
+}
+
+/* Create an OFPT_HELLO message that expresses support for the OpenFlow
+ * versions in the 'allowed_versions' bitmaps and returns the message. */
+struct ofpbuf *
+ofputil_encode_hello(uint32_t allowed_versions)
+{
+ enum ofp_version ofp_version;
+ struct ofpbuf *msg;
+
+ ofp_version = leftmost_1bit_idx(allowed_versions);
+ msg = ofpraw_alloc(OFPRAW_OFPT_HELLO, ofp_version, 0);
+
+ if (should_send_version_bitmap(allowed_versions)) {
+ struct ofp_hello_elem_header *oheh;
+ uint16_t map_len;
+
+ map_len = sizeof allowed_versions;
+ oheh = ofpbuf_put_zeros(msg, ROUND_UP(map_len + sizeof *oheh, 8));
+ oheh->type = htons(OFPHET_VERSIONBITMAP);
+ oheh->length = htons(map_len + sizeof *oheh);
+ *ALIGNED_CAST(ovs_be32 *, oheh + 1) = htonl(allowed_versions);
+
+ ofpmsg_update_length(msg);
+ }
+
+ return msg;
+}
+
+/* Returns an OpenFlow message that, sent on an OpenFlow connection whose
+ * protocol is 'current', at least partly transitions the protocol to 'want'.
+ * Stores in '*next' the protocol that will be in effect on the OpenFlow
+ * connection if the switch processes the returned message correctly. (If
+ * '*next != want' then the caller will have to iterate.)
+ *
+ * If 'current == want', or if it is not possible to transition from 'current'
+ * to 'want' (because, for example, 'current' and 'want' use different OpenFlow
+ * protocol versions), returns NULL and stores 'current' in '*next'. */
+struct ofpbuf *
+ofputil_encode_set_protocol(enum ofputil_protocol current,
+ enum ofputil_protocol want,
+ enum ofputil_protocol *next)
+{
+ enum ofp_version cur_version, want_version;
+ enum ofputil_protocol cur_base, want_base;
+ bool cur_tid, want_tid;
+
+ cur_version = ofputil_protocol_to_ofp_version(current);
+ want_version = ofputil_protocol_to_ofp_version(want);
+ if (cur_version != want_version) {
+ *next = current;
+ return NULL;
+ }
+
+ cur_base = ofputil_protocol_to_base(current);
+ want_base = ofputil_protocol_to_base(want);
+ if (cur_base != want_base) {
+ *next = ofputil_protocol_set_base(current, want_base);
+
+ switch (want_base) {
+ case OFPUTIL_P_OF10_NXM:
+ return ofputil_encode_nx_set_flow_format(NXFF_NXM);
+
+ case OFPUTIL_P_OF10_STD:
+ return ofputil_encode_nx_set_flow_format(NXFF_OPENFLOW10);
+
+ case OFPUTIL_P_OF11_STD:
+ case OFPUTIL_P_OF12_OXM:
+ case OFPUTIL_P_OF13_OXM:
+ /* There is only one variant of each OpenFlow 1.1+ protocol, and we
+ * verified above that we're not trying to change versions. */
+ NOT_REACHED();
+
+ case OFPUTIL_P_OF10_STD_TID:
+ case OFPUTIL_P_OF10_NXM_TID:
+ NOT_REACHED();
+ }
+ }
+
+ cur_tid = (current & OFPUTIL_P_TID) != 0;
+ want_tid = (want & OFPUTIL_P_TID) != 0;
+ if (cur_tid != want_tid) {
+ *next = ofputil_protocol_set_tid(current, want_tid);
+ return ofputil_make_flow_mod_table_id(want_tid);
+ }
+
+ ovs_assert(current == want);
+
+ *next = current;
+ return NULL;
+}
+
+/* Returns an NXT_SET_FLOW_FORMAT message that can be used to set the flow
+ * format to 'nxff'. */
+struct ofpbuf *
+ofputil_encode_nx_set_flow_format(enum nx_flow_format nxff)
+{
+ struct nx_set_flow_format *sff;
+ struct ofpbuf *msg;
+
+ ovs_assert(ofputil_nx_flow_format_is_valid(nxff));
+
+ msg = ofpraw_alloc(OFPRAW_NXT_SET_FLOW_FORMAT, OFP10_VERSION, 0);
+ sff = ofpbuf_put_zeros(msg, sizeof *sff);
+ sff->format = htonl(nxff);
+
+ return msg;
+}
+
+/* Returns the base protocol if 'flow_format' is a valid NXFF_* value, false
+ * otherwise. */
+enum ofputil_protocol
+ofputil_nx_flow_format_to_protocol(enum nx_flow_format flow_format)
+{
+ switch (flow_format) {
+ case NXFF_OPENFLOW10:
+ return OFPUTIL_P_OF10_STD;
+
+ case NXFF_NXM:
+ return OFPUTIL_P_OF10_NXM;
+
+ default:
+ return 0;
+ }
+}
+
+/* Returns true if 'flow_format' is a valid NXFF_* value, false otherwise. */
+bool
+ofputil_nx_flow_format_is_valid(enum nx_flow_format flow_format)
+{
+ return ofputil_nx_flow_format_to_protocol(flow_format) != 0;
+}
+
+/* Returns a string version of 'flow_format', which must be a valid NXFF_*
+ * value. */
+const char *
+ofputil_nx_flow_format_to_string(enum nx_flow_format flow_format)
+{
+ switch (flow_format) {
+ case NXFF_OPENFLOW10:
+ return "openflow10";
+ case NXFF_NXM:
+ return "nxm";
+ default:
+ NOT_REACHED();
+ }
+}
+
+struct ofpbuf *
+ofputil_make_set_packet_in_format(enum ofp_version ofp_version,
+ enum nx_packet_in_format packet_in_format)
+{
+ struct nx_set_packet_in_format *spif;
+ struct ofpbuf *msg;
+
+ msg = ofpraw_alloc(OFPRAW_NXT_SET_PACKET_IN_FORMAT, ofp_version, 0);
+ spif = ofpbuf_put_zeros(msg, sizeof *spif);
+ spif->format = htonl(packet_in_format);
+
+ return msg;
+}
+
+/* Returns an OpenFlow message that can be used to turn the flow_mod_table_id
+ * extension on or off (according to 'flow_mod_table_id'). */
+struct ofpbuf *
+ofputil_make_flow_mod_table_id(bool flow_mod_table_id)
+{
+ struct nx_flow_mod_table_id *nfmti;
+ struct ofpbuf *msg;
+
+ msg = ofpraw_alloc(OFPRAW_NXT_FLOW_MOD_TABLE_ID, OFP10_VERSION, 0);
+ nfmti = ofpbuf_put_zeros(msg, sizeof *nfmti);
+ nfmti->set = flow_mod_table_id;
+ return msg;
+}
+
+/* Converts an OFPT_FLOW_MOD or NXT_FLOW_MOD message 'oh' into an abstract
+ * flow_mod in 'fm'. Returns 0 if successful, otherwise an OpenFlow error
+ * code.
+ *
+ * Uses 'ofpacts' to store the abstract OFPACT_* version of 'oh''s actions.
+ * The caller must initialize 'ofpacts' and retains ownership of it.
+ * 'fm->ofpacts' will point into the 'ofpacts' buffer.
+ *
+ * Does not validate the flow_mod actions. The caller should do that, with
+ * ofpacts_check(). */
+enum ofperr
+ofputil_decode_flow_mod(struct ofputil_flow_mod *fm,
+ const struct ofp_header *oh,
+ enum ofputil_protocol protocol,
+ struct ofpbuf *ofpacts)
+{
+ uint16_t command;
+ struct ofpbuf b;
+ enum ofpraw raw;
+
+ ofpbuf_use_const(&b, oh, ntohs(oh->length));
+ raw = ofpraw_pull_assert(&b);
+ if (raw == OFPRAW_OFPT11_FLOW_MOD) {
+ /* Standard OpenFlow 1.1 flow_mod. */
+ const struct ofp11_flow_mod *ofm;
+ enum ofperr error;
+
+ ofm = ofpbuf_pull(&b, sizeof *ofm);
+
+ error = ofputil_pull_ofp11_match(&b, &fm->match, NULL);
+ if (error) {
+ return error;
+ }
+
+ error = ofpacts_pull_openflow11_instructions(&b, b.size, ofpacts);
+ if (error) {
+ return error;
+ }
+
+ /* Translate the message. */
+ fm->priority = ntohs(ofm->priority);
+ if (ofm->command == OFPFC_ADD
+ || (oh->version == OFP11_VERSION
+ && (ofm->command == OFPFC_MODIFY ||
+ ofm->command == OFPFC_MODIFY_STRICT)
+ && ofm->cookie_mask == htonll(0))) {
+ /* In OpenFlow 1.1 only, a "modify" or "modify-strict" that does
+ * not match on the cookie is treated as an "add" if there is no
+ * match. */
+ fm->cookie = htonll(0);
+ fm->cookie_mask = htonll(0);
+ fm->new_cookie = ofm->cookie;
+ } else {
+ fm->cookie = ofm->cookie;
+ fm->cookie_mask = ofm->cookie_mask;
+ fm->new_cookie = htonll(UINT64_MAX);
+ }
+ fm->modify_cookie = false;
+ fm->command = ofm->command;
+ fm->table_id = ofm->table_id;
+ fm->idle_timeout = ntohs(ofm->idle_timeout);
+ fm->hard_timeout = ntohs(ofm->hard_timeout);
+ fm->buffer_id = ntohl(ofm->buffer_id);
+ error = ofputil_port_from_ofp11(ofm->out_port, &fm->out_port);
+ if (error) {
+ return error;
+ }
+ if ((ofm->command == OFPFC_DELETE
+ || ofm->command == OFPFC_DELETE_STRICT)
+ && ofm->out_group != htonl(OFPG_ANY)) {
+ return OFPERR_OFPFMFC_UNKNOWN;
+ }
+ fm->flags = ntohs(ofm->flags);
+ } else {
+ if (raw == OFPRAW_OFPT10_FLOW_MOD) {
+ /* Standard OpenFlow 1.0 flow_mod. */
+ const struct ofp10_flow_mod *ofm;
+ enum ofperr error;
+
+ /* Get the ofp10_flow_mod. */
+ ofm = ofpbuf_pull(&b, sizeof *ofm);
+
+ /* Translate the rule. */
+ ofputil_match_from_ofp10_match(&ofm->match, &fm->match);
+ ofputil_normalize_match(&fm->match);
+
+ /* Now get the actions. */
+ error = ofpacts_pull_openflow10(&b, b.size, ofpacts);
+ if (error) {
+ return error;
+ }
+
+ /* OpenFlow 1.0 says that exact-match rules have to have the
+ * highest possible priority. */
+ fm->priority = (ofm->match.wildcards & htonl(OFPFW10_ALL)
+ ? ntohs(ofm->priority)
+ : UINT16_MAX);
+
+ /* Translate the message. */
+ command = ntohs(ofm->command);
+ fm->cookie = htonll(0);
+ fm->cookie_mask = htonll(0);
+ fm->new_cookie = ofm->cookie;
+ fm->idle_timeout = ntohs(ofm->idle_timeout);
+ fm->hard_timeout = ntohs(ofm->hard_timeout);
+ fm->buffer_id = ntohl(ofm->buffer_id);
+ fm->out_port = u16_to_ofp(ntohs(ofm->out_port));
+ fm->flags = ntohs(ofm->flags);
+ } else if (raw == OFPRAW_NXT_FLOW_MOD) {
+ /* Nicira extended flow_mod. */
+ const struct nx_flow_mod *nfm;
+ enum ofperr error;
+
+ /* Dissect the message. */
+ nfm = ofpbuf_pull(&b, sizeof *nfm);
+ error = nx_pull_match(&b, ntohs(nfm->match_len),
+ &fm->match, &fm->cookie, &fm->cookie_mask);
+ if (error) {
+ return error;
+ }
+ error = ofpacts_pull_openflow10(&b, b.size, ofpacts);
+ if (error) {
+ return error;
+ }
+
+ /* Translate the message. */
+ command = ntohs(nfm->command);
+ if ((command & 0xff) == OFPFC_ADD && fm->cookie_mask) {
+ /* Flow additions may only set a new cookie, not match an
+ * existing cookie. */
+ return OFPERR_NXBRC_NXM_INVALID;
+ }
+ fm->priority = ntohs(nfm->priority);
+ fm->new_cookie = nfm->cookie;
+ fm->idle_timeout = ntohs(nfm->idle_timeout);
+ fm->hard_timeout = ntohs(nfm->hard_timeout);
+ fm->buffer_id = ntohl(nfm->buffer_id);
+ fm->out_port = u16_to_ofp(ntohs(nfm->out_port));
+ fm->flags = ntohs(nfm->flags);
+ } else {
+ NOT_REACHED();
+ }
+
+ if (fm->flags & OFPFF10_EMERG) {
+ /* We do not support the OpenFlow 1.0 emergency flow cache, which
+ * is not required in OpenFlow 1.0.1 and removed from OpenFlow 1.1.
+ *
+ * OpenFlow 1.0 specifies the error code to use when idle_timeout
+ * or hard_timeout is nonzero. Otherwise, there is no good error
+ * code, so just state that the flow table is full. */
+ return (fm->hard_timeout || fm->idle_timeout
+ ? OFPERR_OFPFMFC_BAD_EMERG_TIMEOUT
+ : OFPERR_OFPFMFC_TABLE_FULL);
+ }
+
+ fm->modify_cookie = fm->new_cookie != htonll(UINT64_MAX);
+ if (protocol & OFPUTIL_P_TID) {
+ fm->command = command & 0xff;
+ fm->table_id = command >> 8;
+ } else {
+ fm->command = command;
+ fm->table_id = 0xff;
+ }
+ }
+
+ fm->ofpacts = ofpacts->data;
+ fm->ofpacts_len = ofpacts->size;
+
+ return 0;
+}
+
+static enum ofperr
+ofputil_pull_bands(struct ofpbuf *msg, size_t len, uint16_t *n_bands,
+ struct ofpbuf *bands)
+{
+ const struct ofp13_meter_band_header *ombh;
+ struct ofputil_meter_band *mb;
+ uint16_t n = 0;
+
+ ombh = ofpbuf_try_pull(msg, len);
+ if (!ombh) {
+ return OFPERR_OFPBRC_BAD_LEN;
+ }
+
+ while (len >= sizeof (struct ofp13_meter_band_drop)) {
+ size_t ombh_len = ntohs(ombh->len);
+ /* All supported band types have the same length. */
+ if (ombh_len != sizeof (struct ofp13_meter_band_drop)) {
+ return OFPERR_OFPBRC_BAD_LEN;
+ }
+ mb = ofpbuf_put_uninit(bands, sizeof *mb);
+ mb->type = ntohs(ombh->type);
+ mb->rate = ntohl(ombh->rate);
+ mb->burst_size = ntohl(ombh->burst_size);
+ mb->prec_level = (mb->type == OFPMBT13_DSCP_REMARK) ?
+ ((struct ofp13_meter_band_dscp_remark *)ombh)->prec_level : 0;
+ n++;
+ len -= ombh_len;
+ ombh = ALIGNED_CAST(struct ofp13_meter_band_header *,
+ (char *) ombh + ombh_len);
+ }
+ if (len) {
+ return OFPERR_OFPBRC_BAD_LEN;
+ }
+ *n_bands = n;
+ return 0;
+}
+
+enum ofperr
+ofputil_decode_meter_mod(const struct ofp_header *oh,
+ struct ofputil_meter_mod *mm,
+ struct ofpbuf *bands)
+{
+ const struct ofp13_meter_mod *omm;
+ struct ofpbuf b;
+
+ ofpbuf_use_const(&b, oh, ntohs(oh->length));
+ ofpraw_pull_assert(&b);
+ omm = ofpbuf_pull(&b, sizeof *omm);
+
+ /* Translate the message. */
+ mm->command = ntohs(omm->command);
+ mm->meter.meter_id = ntohl(omm->meter_id);
+
+ if (mm->command == OFPMC13_DELETE) {
+ mm->meter.flags = 0;
+ mm->meter.n_bands = 0;
+ mm->meter.bands = NULL;
+ } else {
+ enum ofperr error;
+
+ mm->meter.flags = ntohs(omm->flags);
+ mm->meter.bands = bands->data;
+
+ error = ofputil_pull_bands(&b, b.size, &mm->meter.n_bands, bands);
+ if (error) {
+ return error;
+ }
+ }
+ return 0;
+}
+
+void
+ofputil_decode_meter_request(const struct ofp_header *oh, uint32_t *meter_id)
+{
+ const struct ofp13_meter_multipart_request *omr = ofpmsg_body(oh);
+ *meter_id = ntohl(omr->meter_id);
+}
+
+struct ofpbuf *
+ofputil_encode_meter_request(enum ofp_version ofp_version,
+ enum ofputil_meter_request_type type,
+ uint32_t meter_id)
+{
+ struct ofpbuf *msg;
+
+ enum ofpraw raw;
+
+ switch (type) {
+ case OFPUTIL_METER_CONFIG:
+ raw = OFPRAW_OFPST13_METER_CONFIG_REQUEST;
+ break;
+ case OFPUTIL_METER_STATS:
+ raw = OFPRAW_OFPST13_METER_REQUEST;
+ break;
+ default:
+ case OFPUTIL_METER_FEATURES:
+ raw = OFPRAW_OFPST13_METER_FEATURES_REQUEST;
+ break;
+ }
+
+ msg = ofpraw_alloc(raw, ofp_version, 0);
+
+ if (type != OFPUTIL_METER_FEATURES) {
+ struct ofp13_meter_multipart_request *omr;
+ omr = ofpbuf_put_zeros(msg, sizeof *omr);
+ omr->meter_id = htonl(meter_id);
+ }
+ return msg;
+}
+
+static void
+ofputil_put_bands(uint16_t n_bands, const struct ofputil_meter_band *mb,
+ struct ofpbuf *msg)
+{
+ uint16_t n = 0;
+
+ for (n = 0; n < n_bands; ++n) {
+ /* Currently all band types have same size. */
+ struct ofp13_meter_band_dscp_remark *ombh;
+ size_t ombh_len = sizeof *ombh;
+
+ ombh = ofpbuf_put_zeros(msg, ombh_len);
+
+ ombh->type = htons(mb->type);
+ ombh->len = htons(ombh_len);
+ ombh->rate = htonl(mb->rate);
+ ombh->burst_size = htonl(mb->burst_size);
+ ombh->prec_level = mb->prec_level;
+
+ mb++;
+ }
+}
+
+/* Encode a meter stat for 'mc' and append it to 'replies'. */
+void
+ofputil_append_meter_config(struct list *replies,
+ const struct ofputil_meter_config *mc)
+{
+ struct ofpbuf *msg = ofpbuf_from_list(list_back(replies));
+ size_t start_ofs = msg->size;
+ struct ofp13_meter_config *reply = ofpbuf_put_uninit(msg, sizeof *reply);
+ reply->flags = htons(mc->flags);
+ reply->meter_id = htonl(mc->meter_id);
+
+ ofputil_put_bands(mc->n_bands, mc->bands, msg);
+
+ reply->length = htons(msg->size - start_ofs);
+
+ ofpmp_postappend(replies, start_ofs);
+}
+
+/* Encode a meter stat for 'ms' and append it to 'replies'. */
+void
+ofputil_append_meter_stats(struct list *replies,
+ const struct ofputil_meter_stats *ms)
+{
+ struct ofp13_meter_stats *reply;
+ uint16_t n = 0;
+ uint16_t len;
+
+ len = sizeof *reply + ms->n_bands * sizeof(struct ofp13_meter_band_stats);
+ reply = ofpmp_append(replies, len);
+
+ reply->meter_id = htonl(ms->meter_id);
+ reply->len = htons(len);
+ memset(reply->pad, 0, sizeof reply->pad);
+ reply->flow_count = htonl(ms->flow_count);
+ reply->packet_in_count = htonll(ms->packet_in_count);
+ reply->byte_in_count = htonll(ms->byte_in_count);
+ reply->duration_sec = htonl(ms->duration_sec);
+ reply->duration_nsec = htonl(ms->duration_nsec);
+
+ for (n = 0; n < ms->n_bands; ++n) {
+ const struct ofputil_meter_band_stats *src = &ms->bands[n];
+ struct ofp13_meter_band_stats *dst = &reply->band_stats[n];
+
+ dst->packet_band_count = htonll(src->packet_count);
+ dst->byte_band_count = htonll(src->byte_count);
+ }
+}
+
+/* Converts an OFPMP_METER_CONFIG reply in 'msg' into an abstract
+ * ofputil_meter_config in 'mc', with mc->bands pointing to bands decoded into
+ * 'bands'. The caller must have initialized 'bands' and retains ownership of
+ * it across the call.
+ *
+ * Multiple OFPST13_METER_CONFIG replies can be packed into a single OpenFlow
+ * message. Calling this function multiple times for a single 'msg' iterates
+ * through the replies. 'bands' is cleared for each reply.
+ *
+ * Returns 0 if successful, EOF if no replies were left in this 'msg',
+ * otherwise a positive errno value. */
+int
+ofputil_decode_meter_config(struct ofpbuf *msg,
+ struct ofputil_meter_config *mc,
+ struct ofpbuf *bands)
+{
+ const struct ofp13_meter_config *omc;
+ enum ofperr err;
+
+ /* Pull OpenFlow headers for the first call. */
+ if (!msg->l2) {
+ ofpraw_pull_assert(msg);
+ }
+
+ if (!msg->size) {
+ return EOF;
+ }
+
+ omc = ofpbuf_try_pull(msg, sizeof *omc);
+ if (!omc) {
+ VLOG_WARN_RL(&bad_ofmsg_rl,
+ "OFPMP_METER_CONFIG reply has %zu leftover bytes at end",
+ msg->size);
+ return OFPERR_OFPBRC_BAD_LEN;
+ }
+
+ ofpbuf_clear(bands);
+ err = ofputil_pull_bands(msg, ntohs(omc->length) - sizeof *omc,
+ &mc->n_bands, bands);
+ if (err) {
+ return err;
+ }
+ mc->meter_id = ntohl(omc->meter_id);
+ mc->flags = ntohs(omc->flags);
+ mc->bands = bands->data;
+
+ return 0;
+}
+
+static enum ofperr
+ofputil_pull_band_stats(struct ofpbuf *msg, size_t len, uint16_t *n_bands,
+ struct ofpbuf *bands)
+{
+ const struct ofp13_meter_band_stats *ombs;
+ struct ofputil_meter_band_stats *mbs;
+ uint16_t n, i;
+
+ ombs = ofpbuf_try_pull(msg, len);
+ if (!ombs) {
+ return OFPERR_OFPBRC_BAD_LEN;
+ }
+
+ n = len / sizeof *ombs;
+ if (len != n * sizeof *ombs) {
+ return OFPERR_OFPBRC_BAD_LEN;
+ }
+
+ mbs = ofpbuf_put_uninit(bands, len);
+
+ for (i = 0; i < n; ++i) {
+ mbs[i].packet_count = ntohll(ombs[i].packet_band_count);
+ mbs[i].byte_count = ntohll(ombs[i].byte_band_count);
+ }
+ *n_bands = n;
+ return 0;
+}
+
+/* Converts an OFPMP_METER reply in 'msg' into an abstract
+ * ofputil_meter_stats in 'ms', with ms->bands pointing to band stats
+ * decoded into 'bands'.
+ *
+ * Multiple OFPMP_METER replies can be packed into a single OpenFlow
+ * message. Calling this function multiple times for a single 'msg' iterates
+ * through the replies. 'bands' is cleared for each reply.
+ *
+ * Returns 0 if successful, EOF if no replies were left in this 'msg',
+ * otherwise a positive errno value. */
+int
+ofputil_decode_meter_stats(struct ofpbuf *msg,
+ struct ofputil_meter_stats *ms,
+ struct ofpbuf *bands)
+{
+ const struct ofp13_meter_stats *oms;
+ enum ofperr err;
+
+ /* Pull OpenFlow headers for the first call. */
+ if (!msg->l2) {
+ ofpraw_pull_assert(msg);
+ }
+
+ if (!msg->size) {
+ return EOF;
+ }
+
+ oms = ofpbuf_try_pull(msg, sizeof *oms);
+ if (!oms) {
+ VLOG_WARN_RL(&bad_ofmsg_rl,
+ "OFPMP_METER reply has %zu leftover bytes at end",
+ msg->size);
+ return OFPERR_OFPBRC_BAD_LEN;
+ }
+
+ ofpbuf_clear(bands);
+ err = ofputil_pull_band_stats(msg, ntohs(oms->len) - sizeof *oms,
+ &ms->n_bands, bands);
+ if (err) {
+ return err;
+ }
+ ms->meter_id = ntohl(oms->meter_id);
+ ms->flow_count = ntohl(oms->flow_count);
+ ms->packet_in_count = ntohll(oms->packet_in_count);
+ ms->byte_in_count = ntohll(oms->byte_in_count);
+ ms->duration_sec = ntohl(oms->duration_sec);
+ ms->duration_nsec = ntohl(oms->duration_nsec);
+ ms->bands = bands->data;
+
+ return 0;
+}
+
+void
+ofputil_decode_meter_features(const struct ofp_header *oh,
+ struct ofputil_meter_features *mf)
+{
+ const struct ofp13_meter_features *omf = ofpmsg_body(oh);
+
+ mf->max_meters = ntohl(omf->max_meter);
+ mf->band_types = ntohl(omf->band_types);
+ mf->capabilities = ntohl(omf->capabilities);
+ mf->max_bands = omf->max_bands;
+ mf->max_color = omf->max_color;
+}
+
+struct ofpbuf *
+ofputil_encode_meter_features_reply(const struct ofputil_meter_features *mf,
+ const struct ofp_header *request)
+{
+ struct ofpbuf *reply;
+ struct ofp13_meter_features *omf;
+
+ reply = ofpraw_alloc_stats_reply(request, 0);
+ omf = ofpbuf_put_zeros(reply, sizeof *omf);
+
+ omf->max_meter = htonl(mf->max_meters);
+ omf->band_types = htonl(mf->band_types);
+ omf->capabilities = htonl(mf->capabilities);
+ omf->max_bands = mf->max_bands;
+ omf->max_color = mf->max_color;
+
+ return reply;
+}
+
+struct ofpbuf *
+ofputil_encode_meter_mod(enum ofp_version ofp_version,
+ const struct ofputil_meter_mod *mm)
+{
+ struct ofpbuf *msg;
+
+ struct ofp13_meter_mod *omm;
+
+ msg = ofpraw_alloc(OFPRAW_OFPT13_METER_MOD, ofp_version,
+ NXM_TYPICAL_LEN + mm->meter.n_bands * 16);
+ omm = ofpbuf_put_zeros(msg, sizeof *omm);
+ omm->command = htons(mm->command);
+ if (mm->command != OFPMC13_DELETE) {
+ omm->flags = htons(mm->meter.flags);
+ }
+ omm->meter_id = htonl(mm->meter.meter_id);
+
+ ofputil_put_bands(mm->meter.n_bands, mm->meter.bands, msg);
+
+ ofpmsg_update_length(msg);
+ return msg;
+}
+
+static ovs_be16
+ofputil_tid_command(const struct ofputil_flow_mod *fm,
+ enum ofputil_protocol protocol)
+{
+ return htons(protocol & OFPUTIL_P_TID
+ ? (fm->command & 0xff) | (fm->table_id << 8)
+ : fm->command);
+}
+
+/* Converts 'fm' into an OFPT_FLOW_MOD or NXT_FLOW_MOD message according to
+ * 'protocol' and returns the message. */
+struct ofpbuf *
+ofputil_encode_flow_mod(const struct ofputil_flow_mod *fm,
+ 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 ofp11_flow_mod *ofm;
+ int tailroom;
+
+ tailroom = ofputil_match_typical_len(protocol) + fm->ofpacts_len;
+ msg = ofpraw_alloc(OFPRAW_OFPT11_FLOW_MOD,
+ ofputil_protocol_to_ofp_version(protocol),
+ tailroom);
+ ofm = ofpbuf_put_zeros(msg, sizeof *ofm);
+ if ((protocol == OFPUTIL_P_OF11_STD
+ && (fm->command == OFPFC_MODIFY ||
+ fm->command == OFPFC_MODIFY_STRICT)
+ && fm->cookie_mask == htonll(0))
+ || fm->command == OFPFC_ADD) {
+ ofm->cookie = fm->new_cookie;
+ } else {
+ ofm->cookie = fm->cookie;
+ }
+ ofm->cookie_mask = fm->cookie_mask;
+ ofm->table_id = fm->table_id;
+ ofm->command = fm->command;
+ ofm->idle_timeout = htons(fm->idle_timeout);
+ ofm->hard_timeout = htons(fm->hard_timeout);
+ ofm->priority = htons(fm->priority);
+ ofm->buffer_id = htonl(fm->buffer_id);
+ ofm->out_port = ofputil_port_to_ofp11(fm->out_port);
+ ofm->out_group = htonl(OFPG11_ANY);
+ ofm->flags = htons(fm->flags);
+ ofputil_put_ofp11_match(msg, &fm->match, protocol);
+ ofpacts_put_openflow11_instructions(fm->ofpacts, fm->ofpacts_len, msg);
+ break;
+ }
+
+ case OFPUTIL_P_OF10_STD:
+ case OFPUTIL_P_OF10_STD_TID: {
+ struct ofp10_flow_mod *ofm;
+
+ msg = ofpraw_alloc(OFPRAW_OFPT10_FLOW_MOD, OFP10_VERSION,
+ fm->ofpacts_len);
+ ofm = ofpbuf_put_zeros(msg, sizeof *ofm);
+ ofputil_match_to_ofp10_match(&fm->match, &ofm->match);
+ ofm->cookie = fm->new_cookie;
+ ofm->command = ofputil_tid_command(fm, protocol);
+ ofm->idle_timeout = htons(fm->idle_timeout);
+ ofm->hard_timeout = htons(fm->hard_timeout);
+ ofm->priority = htons(fm->priority);
+ ofm->buffer_id = htonl(fm->buffer_id);
+ ofm->out_port = htons(ofp_to_u16(fm->out_port));
+ ofm->flags = htons(fm->flags);
+ ofpacts_put_openflow10(fm->ofpacts, fm->ofpacts_len, msg);
+ break;
+ }
+
+ case OFPUTIL_P_OF10_NXM:
+ case OFPUTIL_P_OF10_NXM_TID: {
+ struct nx_flow_mod *nfm;
+ int match_len;
+
+ msg = ofpraw_alloc(OFPRAW_NXT_FLOW_MOD, OFP10_VERSION,
+ NXM_TYPICAL_LEN + fm->ofpacts_len);
+ nfm = ofpbuf_put_zeros(msg, sizeof *nfm);
+ nfm->command = ofputil_tid_command(fm, protocol);
+ nfm->cookie = fm->new_cookie;
+ match_len = nx_put_match(msg, &fm->match, fm->cookie, fm->cookie_mask);
+ nfm = msg->l3;
+ nfm->idle_timeout = htons(fm->idle_timeout);
+ nfm->hard_timeout = htons(fm->hard_timeout);
+ nfm->priority = htons(fm->priority);
+ nfm->buffer_id = htonl(fm->buffer_id);
+ nfm->out_port = htons(ofp_to_u16(fm->out_port));
+ nfm->flags = htons(fm->flags);
+ nfm->match_len = htons(match_len);
+ ofpacts_put_openflow10(fm->ofpacts, fm->ofpacts_len, msg);
+ break;
+ }
+
+ default:
+ NOT_REACHED();
+ }
+
+ ofpmsg_update_length(msg);
+ return msg;
+}
+
+/* Returns a bitmask with a 1-bit for each protocol that could be used to
+ * send all of the 'n_fm's flow table modification requests in 'fms', and a
+ * 0-bit for each protocol that is inadequate.
+ *
+ * (The return value will have at least one 1-bit.) */
+enum ofputil_protocol
+ofputil_flow_mod_usable_protocols(const struct ofputil_flow_mod *fms,
+ size_t n_fms)
+{
+ enum ofputil_protocol usable_protocols;
+ size_t i;
+
+ usable_protocols = OFPUTIL_P_ANY;
+ for (i = 0; i < n_fms; i++) {
+ const struct ofputil_flow_mod *fm = &fms[i];
+
+ usable_protocols &= ofputil_usable_protocols(&fm->match);
+ if (fm->table_id != 0xff) {
+ usable_protocols &= OFPUTIL_P_TID;
+ }
+
+ /* Matching of the cookie is only supported through NXM or OF1.1+. */
+ if (fm->cookie_mask != htonll(0)) {
+ usable_protocols &= (OFPUTIL_P_OF10_NXM_ANY
+ | OFPUTIL_P_OF11_STD
+ | OFPUTIL_P_OF12_OXM
+ | OFPUTIL_P_OF13_OXM);
+ }
+ }
+
+ return usable_protocols;
+}
+
+static enum ofperr
+ofputil_decode_ofpst10_flow_request(struct ofputil_flow_stats_request *fsr,
+ const struct ofp10_flow_stats_request *ofsr,
+ bool aggregate)
+{
+ fsr->aggregate = aggregate;
+ ofputil_match_from_ofp10_match(&ofsr->match, &fsr->match);
+ fsr->out_port = u16_to_ofp(ntohs(ofsr->out_port));
+ fsr->table_id = ofsr->table_id;
+ fsr->cookie = fsr->cookie_mask = htonll(0);
+
+ return 0;
+}
+
+static enum ofperr
+ofputil_decode_ofpst11_flow_request(struct ofputil_flow_stats_request *fsr,
+ struct ofpbuf *b, bool aggregate)
+{
+ const struct ofp11_flow_stats_request *ofsr;
+ enum ofperr error;
+
+ ofsr = ofpbuf_pull(b, sizeof *ofsr);
+ fsr->aggregate = aggregate;
+ fsr->table_id = ofsr->table_id;
+ error = ofputil_port_from_ofp11(ofsr->out_port, &fsr->out_port);
+ if (error) {
+ return error;
+ }
+ if (ofsr->out_group != htonl(OFPG11_ANY)) {
+ return OFPERR_OFPFMFC_UNKNOWN;
+ }
+ fsr->cookie = ofsr->cookie;
+ fsr->cookie_mask = ofsr->cookie_mask;
+ error = ofputil_pull_ofp11_match(b, &fsr->match, NULL);
+ if (error) {
+ return error;
+ }
+
+ return 0;
+}
+
+static enum ofperr
+ofputil_decode_nxst_flow_request(struct ofputil_flow_stats_request *fsr,
+ struct ofpbuf *b, bool aggregate)
+{
+ const struct nx_flow_stats_request *nfsr;
+ enum ofperr error;
+
+ nfsr = ofpbuf_pull(b, sizeof *nfsr);
+ error = nx_pull_match(b, ntohs(nfsr->match_len), &fsr->match,
+ &fsr->cookie, &fsr->cookie_mask);
+ if (error) {
+ return error;
+ }
+ if (b->size) {
+ return OFPERR_OFPBRC_BAD_LEN;
+ }
+
+ fsr->aggregate = aggregate;
+ fsr->out_port = u16_to_ofp(ntohs(nfsr->out_port));
+ fsr->table_id = nfsr->table_id;
+
+ return 0;
+}
+
+/* Converts an OFPST_FLOW, OFPST_AGGREGATE, NXST_FLOW, or NXST_AGGREGATE
+ * request 'oh', into an abstract flow_stats_request in 'fsr'. Returns 0 if
+ * successful, otherwise an OpenFlow error code. */
+enum ofperr
+ofputil_decode_flow_stats_request(struct ofputil_flow_stats_request *fsr,
+ const struct ofp_header *oh)
+{
+ enum ofpraw raw;
+ struct ofpbuf b;
+
+ ofpbuf_use_const(&b, oh, ntohs(oh->length));
+ raw = ofpraw_pull_assert(&b);
+ switch ((int) raw) {
+ case OFPRAW_OFPST10_FLOW_REQUEST:
+ return ofputil_decode_ofpst10_flow_request(fsr, b.data, false);
+
+ case OFPRAW_OFPST10_AGGREGATE_REQUEST:
+ return ofputil_decode_ofpst10_flow_request(fsr, b.data, true);
+
+ case OFPRAW_OFPST11_FLOW_REQUEST:
+ return ofputil_decode_ofpst11_flow_request(fsr, &b, false);
+
+ case OFPRAW_OFPST11_AGGREGATE_REQUEST:
+ return ofputil_decode_ofpst11_flow_request(fsr, &b, true);
+
+ case OFPRAW_NXST_FLOW_REQUEST:
+ return ofputil_decode_nxst_flow_request(fsr, &b, false);
+
+ case OFPRAW_NXST_AGGREGATE_REQUEST:
+ return ofputil_decode_nxst_flow_request(fsr, &b, true);
+
+ default:
+ /* Hey, the caller lied. */
+ NOT_REACHED();
+ }
+}
+
+/* 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(OFPG11_ANY);
+ 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;
+}
+
+/* Returns a bitmask with a 1-bit for each protocol that could be used to
+ * accurately encode 'fsr', and a 0-bit for each protocol that is inadequate.
+ *
+ * (The return value will have at least one 1-bit.) */
+enum ofputil_protocol
+ofputil_flow_stats_request_usable_protocols(
+ const struct ofputil_flow_stats_request *fsr)
+{
+ enum ofputil_protocol usable_protocols;
+
+ usable_protocols = ofputil_usable_protocols(&fsr->match);
+ if (fsr->cookie_mask != htonll(0)) {
+ usable_protocols &= OFPUTIL_P_OF10_NXM_ANY | OFPUTIL_P_OF12_OXM
+ | OFPUTIL_P_OF13_OXM;
+ }
+ return usable_protocols;
+}
+
+/* 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)
+{
+ 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_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_openflow11_instructions(msg, length - sizeof *ofs -
+ padded_match_len, 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);
+ fs->flags = (raw == OFPRAW_OFPST13_FLOW_REPLY) ? ntohs(ofs->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_openflow10(msg, length - sizeof *ofs, 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_openflow10(msg, actions_len, 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 ofputil_start_stats_reply(). */
+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;
+
+ 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_openflow11_instructions(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;
+ 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);
+ ofs->flags = (raw == OFPRAW_OFPST13_FLOW_REPLY) ? htons(fs->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_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_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_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->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);