Expand tunnel IDs from 32 to 64 bits.
[sliver-openvswitch.git] / lib / ofp-util.c
index 71e5767..b4b7ecd 100644 (file)
@@ -25,6 +25,7 @@
 #include "ofpbuf.h"
 #include "packets.h"
 #include "random.h"
+#include "type-props.h"
 #include "vlog.h"
 
 VLOG_DEFINE_THIS_MODULE(ofp_util);
@@ -105,7 +106,7 @@ void
 ofputil_cls_rule_from_match(const struct ofp_match *match,
                             unsigned int priority,
                             enum nx_flow_format flow_format,
-                            uint64_t cookie, struct cls_rule *rule)
+                            ovs_be64 cookie, struct cls_rule *rule)
 {
     struct flow_wildcards *wc = &rule->wc;
     unsigned int ofpfw;
@@ -117,19 +118,19 @@ ofputil_cls_rule_from_match(const struct ofp_match *match,
     rule->priority = !ofpfw ? UINT16_MAX : priority;
 
     /* Initialize most of rule->wc. */
+    flow_wildcards_init_catchall(wc);
     wc->wildcards = ofpfw & WC_INVARIANTS;
     if (ofpfw & OFPFW_NW_TOS) {
         wc->wildcards |= FWW_NW_TOS;
     }
-    memset(wc->reg_masks, 0, sizeof wc->reg_masks);
     wc->nw_src_mask = ofputil_wcbits_to_netmask(ofpfw >> OFPFW_NW_SRC_SHIFT);
     wc->nw_dst_mask = ofputil_wcbits_to_netmask(ofpfw >> OFPFW_NW_DST_SHIFT);
 
     if (flow_format == NXFF_TUN_ID_FROM_COOKIE && !(ofpfw & NXFW_TUN_ID)) {
-        rule->flow.tun_id = htonl(ntohll(cookie) >> 32);
+        rule->flow.tun_id = htonll(ntohll(cookie) >> 32);
     } else {
         wc->wildcards |= FWW_TUN_ID;
-        rule->flow.tun_id = 0;
+        rule->flow.tun_id = htonll(0);
     }
 
     if (ofpfw & OFPFW_DL_DST) {
@@ -199,16 +200,21 @@ ofputil_cls_rule_from_match(const struct ofp_match *match,
     cls_rule_zero_wildcarded_fields(rule);
 }
 
-/* Extract 'flow' with 'wildcards' into the OpenFlow match structure
- * 'match'.
+/* Convert 'rule' into the OpenFlow match structure 'match'.  'flow_format'
+ * must either NXFF_OPENFLOW10 or NXFF_TUN_ID_FROM_COOKIE.
  *
- * 'flow_format' must either NXFF_OPENFLOW10 or NXFF_TUN_ID_FROM_COOKIE.  In
- * the latter case only, 'match''s NXFW_TUN_ID bit will be filled in; otherwise
- * it is always set to 0. */
+ * The NXFF_TUN_ID_FROM_COOKIE flow format requires modifying the flow cookie.
+ * This function can help with that, if 'cookie_out' is nonnull.  For
+ * NXFF_OPENFLOW10, or if the tunnel ID is wildcarded, 'cookie_in' will be
+ * copied directly to '*cookie_out'.  For NXFF_TUN_ID_FROM_COOKIE when tunnel
+ * ID is matched, 'cookie_in' will be modified appropriately before setting
+ * '*cookie_out'.
+ */
 void
 ofputil_cls_rule_to_match(const struct cls_rule *rule,
                           enum nx_flow_format flow_format,
-                          struct ofp_match *match)
+                          struct ofp_match *match,
+                          ovs_be64 cookie_in, ovs_be64 *cookie_out)
 {
     const struct flow_wildcards *wc = &rule->wc;
     unsigned int ofpfw;
@@ -220,8 +226,19 @@ ofputil_cls_rule_to_match(const struct cls_rule *rule,
     if (wc->wildcards & FWW_NW_TOS) {
         ofpfw |= OFPFW_NW_TOS;
     }
-    if (flow_format == NXFF_TUN_ID_FROM_COOKIE && wc->wildcards & FWW_TUN_ID) {
-        ofpfw |= NXFW_TUN_ID;
+
+    /* Tunnel ID. */
+    if (flow_format == NXFF_TUN_ID_FROM_COOKIE) {
+        if (wc->wildcards & FWW_TUN_ID) {
+            ofpfw |= NXFW_TUN_ID;
+        } else {
+            uint32_t cookie_lo = ntohll(cookie_in);
+            uint32_t cookie_hi = ntohll(rule->flow.tun_id);
+            cookie_in = htonll(cookie_lo | ((uint64_t) cookie_hi << 32));
+        }
+    }
+    if (cookie_out) {
+        *cookie_out = cookie_in;
     }
 
     /* Translate VLANs. */
@@ -755,6 +772,450 @@ ofputil_msg_type_code(const struct ofputil_msg_type *type)
 {
     return type->code;
 }
+\f
+/* Flow formats. */
+
+bool
+ofputil_flow_format_is_valid(enum nx_flow_format flow_format)
+{
+    switch (flow_format) {
+    case NXFF_OPENFLOW10:
+    case NXFF_TUN_ID_FROM_COOKIE:
+    case NXFF_NXM:
+        return true;
+    }
+
+    return false;
+}
+
+const char *
+ofputil_flow_format_to_string(enum nx_flow_format flow_format)
+{
+    switch (flow_format) {
+    case NXFF_OPENFLOW10:
+        return "openflow10";
+    case NXFF_TUN_ID_FROM_COOKIE:
+        return "tun_id_from_cookie";
+    case NXFF_NXM:
+        return "nxm";
+    default:
+        NOT_REACHED();
+    }
+}
+
+int
+ofputil_flow_format_from_string(const char *s)
+{
+    return (!strcmp(s, "openflow10") ? NXFF_OPENFLOW10
+            : !strcmp(s, "tun_id_from_cookie") ? NXFF_TUN_ID_FROM_COOKIE
+            : !strcmp(s, "nxm") ? NXFF_NXM
+            : -1);
+}
+
+static bool
+regs_fully_wildcarded(const struct flow_wildcards *wc)
+{
+    int i;
+
+    for (i = 0; i < FLOW_N_REGS; i++) {
+        if (wc->reg_masks[i] != 0) {
+            return false;
+        }
+    }
+    return true;
+}
+
+/* Returns the minimum nx_flow_format to use for sending 'rule' to a switch
+ * (e.g. to add or remove a flow).  'cookie_support' should be true if the
+ * command to be sent includes a flow cookie (as OFPT_FLOW_MOD does, for
+ * example) or false if the command does not (OFPST_FLOW and OFPST_AGGREGATE do
+ * not, for example).  If 'cookie_support' is true, then 'cookie' should be the
+ * cookie to be sent; otherwise its value is ignored.
+ *
+ * The "best" flow format is chosen on this basis:
+ *
+ *   - It must be capable of expressing the rule.  NXFF_OPENFLOW10 flows can't
+ *     handle tunnel IDs.  NXFF_TUN_ID_FROM_COOKIE flows can't handle registers
+ *     or fixing the Ethernet multicast bit, and can't handle tunnel IDs that
+ *     conflict with the high 32 bits of the cookie or commands that don't
+ *     support cookies.
+ *
+ *   - Otherwise, the chosen format should be as backward compatible as
+ *     possible.  (NXFF_OPENFLOW10 is more backward compatible than
+ *     NXFF_TUN_ID_FROM_COOKIE, which is more backward compatible than
+ *     NXFF_NXM.)
+ */
+enum nx_flow_format
+ofputil_min_flow_format(const struct cls_rule *rule, bool cookie_support,
+                        ovs_be64 cookie)
+{
+    const struct flow_wildcards *wc = &rule->wc;
+    ovs_be32 cookie_hi = htonl(ntohll(cookie) >> 32);
+
+    if (!(wc->wildcards & FWW_DL_DST) != !(wc->wildcards & FWW_ETH_MCAST)
+        || !regs_fully_wildcarded(wc)
+        || (!(wc->wildcards & FWW_TUN_ID)
+            && (!cookie_support
+                || (cookie_hi && cookie_hi != ntohll(rule->flow.tun_id))))) {
+        return NXFF_NXM;
+    } else if (!(wc->wildcards & FWW_TUN_ID)) {
+        return NXFF_TUN_ID_FROM_COOKIE;
+    } else {
+        return NXFF_OPENFLOW10;
+    }
+}
+
+/* Returns an OpenFlow message that can be used to set the flow format to
+ * 'flow_format'.  */
+struct ofpbuf *
+ofputil_make_set_flow_format(enum nx_flow_format flow_format)
+{
+    struct ofpbuf *msg;
+
+    if (flow_format == NXFF_OPENFLOW10
+        || flow_format == NXFF_TUN_ID_FROM_COOKIE) {
+        struct nxt_tun_id_cookie *tic;
+
+        tic = make_nxmsg(sizeof *tic, NXT_TUN_ID_FROM_COOKIE, &msg);
+        tic->set = flow_format == NXFF_TUN_ID_FROM_COOKIE;
+    } else {
+        struct nxt_set_flow_format *sff;
+
+        sff = make_nxmsg(sizeof *sff, NXT_SET_FLOW_FORMAT, &msg);
+        sff->format = htonl(flow_format);
+    }
+
+    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.
+ *
+ * For OFPT_FLOW_MOD messages, 'flow_format' should be the current flow format
+ * at the time when the message was received.  Otherwise 'flow_format' is
+ * ignored.
+ *
+ * Does not validate the flow_mod actions. */
+int
+ofputil_decode_flow_mod(struct flow_mod *fm, const struct ofp_header *oh,
+                        enum nx_flow_format flow_format)
+{
+    const struct ofputil_msg_type *type;
+    struct ofpbuf b;
+
+    ofpbuf_use_const(&b, oh, ntohs(oh->length));
+
+    ofputil_decode_msg_type(oh, &type);
+    if (ofputil_msg_type_code(type) == OFPUTIL_OFPT_FLOW_MOD) {
+        /* Standard OpenFlow flow_mod. */
+        struct ofp_match match, orig_match;
+        const struct ofp_flow_mod *ofm;
+        int error;
+
+        /* Dissect the message. */
+        ofm = ofpbuf_pull(&b, sizeof *ofm);
+        error = ofputil_pull_actions(&b, b.size, &fm->actions, &fm->n_actions);
+        if (error) {
+            return error;
+        }
+
+        /* Normalize ofm->match.  If normalization actually changes anything,
+         * then log the differences. */
+        match = ofm->match;
+        match.pad1[0] = match.pad2[0] = 0;
+        orig_match = match;
+        normalize_match(&match);
+        if (memcmp(&match, &orig_match, sizeof orig_match)) {
+            if (!VLOG_DROP_INFO(&bad_ofmsg_rl)) {
+                char *old = ofp_match_to_literal_string(&orig_match);
+                char *new = ofp_match_to_literal_string(&match);
+                VLOG_INFO("normalization changed ofp_match, details:");
+                VLOG_INFO(" pre: %s", old);
+                VLOG_INFO("post: %s", new);
+                free(old);
+                free(new);
+            }
+        }
+
+        /* Translate the message. */
+        ofputil_cls_rule_from_match(&match, ntohs(ofm->priority), flow_format,
+                                    ofm->cookie, &fm->cr);
+        fm->cookie = ofm->cookie;
+        fm->command = ntohs(ofm->command);
+        fm->idle_timeout = ntohs(ofm->idle_timeout);
+        fm->hard_timeout = ntohs(ofm->hard_timeout);
+        fm->buffer_id = ntohl(ofm->buffer_id);
+        fm->out_port = ntohs(ofm->out_port);
+        fm->flags = ntohs(ofm->flags);
+    } else if (ofputil_msg_type_code(type) == OFPUTIL_NXT_FLOW_MOD) {
+        /* Nicira extended flow_mod. */
+        const struct nx_flow_mod *nfm;
+        int error;
+
+        /* Dissect the message. */
+        nfm = ofpbuf_pull(&b, sizeof *nfm);
+        error = nx_pull_match(&b, ntohs(nfm->match_len), ntohs(nfm->priority),
+                              &fm->cr);
+        if (error) {
+            return error;
+        }
+        error = ofputil_pull_actions(&b, b.size, &fm->actions, &fm->n_actions);
+        if (error) {
+            return error;
+        }
+
+        /* Translate the message. */
+        fm->cookie = nfm->cookie;
+        fm->command = ntohs(nfm->command);
+        fm->idle_timeout = ntohs(nfm->idle_timeout);
+        fm->hard_timeout = ntohs(nfm->hard_timeout);
+        fm->buffer_id = ntohl(nfm->buffer_id);
+        fm->out_port = ntohs(nfm->out_port);
+        fm->flags = ntohs(nfm->flags);
+    } else {
+        NOT_REACHED();
+    }
+
+    return 0;
+}
+
+/* Converts 'fm' into an OFPT_FLOW_MOD or NXT_FLOW_MOD message according to
+ * 'flow_format' and returns the message. */
+struct ofpbuf *
+ofputil_encode_flow_mod(const struct flow_mod *fm,
+                        enum nx_flow_format flow_format)
+{
+    size_t actions_len = fm->n_actions * sizeof *fm->actions;
+    struct ofpbuf *msg;
+
+    if (flow_format == NXFF_OPENFLOW10
+        || flow_format == NXFF_TUN_ID_FROM_COOKIE) {
+        struct ofp_flow_mod *ofm;
+
+        msg = ofpbuf_new(sizeof *ofm + actions_len);
+        ofm = put_openflow(sizeof *ofm, OFPT_FLOW_MOD, msg);
+        ofputil_cls_rule_to_match(&fm->cr, flow_format, &ofm->match,
+                                  fm->cookie, &ofm->cookie);
+        ofm->command = htons(fm->command);
+        ofm->idle_timeout = htons(fm->idle_timeout);
+        ofm->hard_timeout = htons(fm->hard_timeout);
+        ofm->priority = htons(fm->cr.priority);
+        ofm->buffer_id = htonl(fm->buffer_id);
+        ofm->out_port = htons(fm->out_port);
+        ofm->flags = htons(fm->flags);
+    } else if (flow_format == NXFF_NXM) {
+        struct nx_flow_mod *nfm;
+        int match_len;
+
+        msg = ofpbuf_new(sizeof *nfm + NXM_TYPICAL_LEN + actions_len);
+        put_nxmsg(sizeof *nfm, NXT_FLOW_MOD, msg);
+        match_len = nx_put_match(msg, &fm->cr);
+
+        nfm = msg->data;
+        nfm->cookie = fm->cookie;
+        nfm->command = htons(fm->command);
+        nfm->idle_timeout = htons(fm->idle_timeout);
+        nfm->hard_timeout = htons(fm->hard_timeout);
+        nfm->priority = htons(fm->cr.priority);
+        nfm->buffer_id = htonl(fm->buffer_id);
+        nfm->out_port = htons(fm->out_port);
+        nfm->flags = htons(fm->flags);
+        nfm->match_len = htons(match_len);
+    } else {
+        NOT_REACHED();
+    }
+
+    ofpbuf_put(msg, fm->actions, actions_len);
+    update_openflow_length(msg);
+    return msg;
+}
+
+static int
+ofputil_decode_ofpst_flow_request(struct flow_stats_request *fsr,
+                                  const struct ofp_header *oh,
+                                  enum nx_flow_format flow_format,
+                                  bool aggregate)
+{
+    const struct ofp_flow_stats_request *ofsr = ofputil_stats_body(oh);
+
+    fsr->aggregate = aggregate;
+    ofputil_cls_rule_from_match(&ofsr->match, 0, flow_format, 0, &fsr->match);
+    fsr->out_port = ntohs(ofsr->out_port);
+    fsr->table_id = ofsr->table_id;
+
+    return 0;
+}
+
+static int
+ofputil_decode_nxst_flow_request(struct flow_stats_request *fsr,
+                                 const struct ofp_header *oh,
+                                 bool aggregate)
+{
+    const struct nx_flow_stats_request *nfsr;
+    struct ofpbuf b;
+    int error;
+
+    ofpbuf_use_const(&b, oh, ntohs(oh->length));
+
+    nfsr = ofpbuf_pull(&b, sizeof *nfsr);
+    error = nx_pull_match(&b, ntohs(nfsr->match_len), 0, &fsr->match);
+    if (error) {
+        return error;
+    }
+    if (b.size) {
+        return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_LEN);
+    }
+
+    fsr->aggregate = aggregate;
+    fsr->out_port = ntohs(nfsr->out_port);
+    fsr->table_id = nfsr->table_id;
+
+    return 0;
+}
+
+/* Converts an OFPST_FLOW, OFPST_AGGREGATE, NXST_FLOW, or NXST_AGGREGATE
+ * message 'oh', received when the current flow format was 'flow_format', into
+ * an abstract flow_stats_request in 'fsr'.  Returns 0 if successful, otherwise
+ * an OpenFlow error code.
+ *
+ * For OFPST_FLOW and OFPST_AGGREGATE messages, 'flow_format' should be the
+ * current flow format at the time when the message was received.  Otherwise
+ * 'flow_format' is ignored. */
+int
+ofputil_decode_flow_stats_request(struct flow_stats_request *fsr,
+                                  const struct ofp_header *oh,
+                                  enum nx_flow_format flow_format)
+{
+    const struct ofputil_msg_type *type;
+    struct ofpbuf b;
+    int code;
+
+    ofpbuf_use_const(&b, oh, ntohs(oh->length));
+
+    ofputil_decode_msg_type(oh, &type);
+    code = ofputil_msg_type_code(type);
+    switch (code) {
+    case OFPUTIL_OFPST_FLOW_REQUEST:
+        return ofputil_decode_ofpst_flow_request(fsr, oh, flow_format, false);
+
+    case OFPUTIL_OFPST_AGGREGATE_REQUEST:
+        return ofputil_decode_ofpst_flow_request(fsr, oh, flow_format, true);
+
+    case OFPUTIL_NXST_FLOW_REQUEST:
+        return ofputil_decode_nxst_flow_request(fsr, oh, false);
+
+    case OFPUTIL_NXST_AGGREGATE_REQUEST:
+        return ofputil_decode_nxst_flow_request(fsr, oh, 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 message 'oh' according to
+ * 'flow_format', and returns the message. */
+struct ofpbuf *
+ofputil_encode_flow_stats_request(const struct flow_stats_request *fsr,
+                                  enum nx_flow_format flow_format)
+{
+    struct ofpbuf *msg;
+
+    if (flow_format == NXFF_OPENFLOW10
+        || flow_format == NXFF_TUN_ID_FROM_COOKIE) {
+        struct ofp_flow_stats_request *ofsr;
+        int type;
+
+        BUILD_ASSERT_DECL(sizeof(struct ofp_flow_stats_request)
+                          == sizeof(struct ofp_aggregate_stats_request));
+
+        type = fsr->aggregate ? OFPST_AGGREGATE : OFPST_FLOW;
+        ofsr = ofputil_make_stats_request(sizeof *ofsr, type, &msg);
+        ofputil_cls_rule_to_match(&fsr->match, flow_format, &ofsr->match,
+                                  0, NULL);
+        ofsr->table_id = fsr->table_id;
+        ofsr->out_port = htons(fsr->out_port);
+    } else if (flow_format == NXFF_NXM) {
+        struct nx_flow_stats_request *nfsr;
+        int match_len;
+
+        ofputil_make_nxstats_request(sizeof *nfsr, NXST_FLOW, &msg);
+        match_len = nx_put_match(msg, &fsr->match);
+
+        nfsr = msg->data;
+        nfsr->out_port = htons(fsr->out_port);
+        nfsr->match_len = htons(match_len);
+        nfsr->table_id = fsr->table_id;
+    } else {
+        NOT_REACHED();
+    }
+
+    return msg;
+}
+
+/* Converts an OFPT_FLOW_REMOVED or NXT_FLOW_REMOVED message 'oh', received
+ * when the current flow format was 'flow_format', into an abstract
+ * ofputil_flow_removed in 'fr'.  Returns 0 if successful, otherwise an
+ * OpenFlow error code.
+ *
+ * For OFPT_FLOW_REMOVED messages, 'flow_format' should be the current flow
+ * format at the time when the message was received.  Otherwise 'flow_format'
+ * is ignored. */
+int
+ofputil_decode_flow_removed(struct ofputil_flow_removed *fr,
+                            const struct ofp_header *oh,
+                            enum nx_flow_format flow_format)
+{
+    const struct ofputil_msg_type *type;
+    enum ofputil_msg_code code;
+
+    ofputil_decode_msg_type(oh, &type);
+    code = ofputil_msg_type_code(type);
+    if (code == OFPUTIL_OFPT_FLOW_REMOVED) {
+        const struct ofp_flow_removed *ofr;
+
+        ofr = (const struct ofp_flow_removed *) oh;
+        ofputil_cls_rule_from_match(&ofr->match, ntohs(ofr->priority),
+                                    flow_format, ofr->cookie, &fr->rule);
+        fr->cookie = ofr->cookie;
+        fr->reason = ofr->reason;
+        fr->duration_sec = ntohl(ofr->duration_sec);
+        fr->duration_nsec = ntohl(ofr->duration_nsec);
+        fr->idle_timeout = ntohs(ofr->idle_timeout);
+        fr->packet_count = ntohll(ofr->packet_count);
+        fr->byte_count = ntohll(ofr->byte_count);
+    } else if (code == OFPUTIL_NXT_FLOW_REMOVED) {
+        struct nx_flow_removed *nfr;
+        struct ofpbuf b;
+        int error;
+
+        ofpbuf_use_const(&b, oh, ntohs(oh->length));
+
+        nfr = ofpbuf_pull(&b, sizeof *nfr);
+        error = nx_pull_match(&b, ntohs(nfr->match_len), ntohs(nfr->priority),
+                              &fr->rule);
+        if (error) {
+            return error;
+        }
+        if (b.size) {
+            return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_LEN);
+        }
+
+        fr->cookie = nfr->cookie;
+        fr->reason = nfr->reason;
+        fr->duration_sec = ntohl(nfr->duration_sec);
+        fr->duration_nsec = ntohl(nfr->duration_nsec);
+        fr->idle_timeout = ntohs(nfr->idle_timeout);
+        fr->packet_count = ntohll(nfr->packet_count);
+        fr->byte_count = ntohll(nfr->byte_count);
+    } else {
+        NOT_REACHED();
+    }
+
+    return 0;
+}
 
 /* Returns a string representing the message type of 'type'.  The string is the
  * enumeration constant for the type, e.g. "OFPT_HELLO".  For statistics
@@ -821,11 +1282,8 @@ void *
 make_nxmsg_xid(size_t openflow_len, uint32_t subtype, ovs_be32 xid,
                struct ofpbuf **bufferp)
 {
-    struct nicira_header *nxh = make_openflow_xid(openflow_len, OFPT_VENDOR,
-                                                  xid, bufferp);
-    nxh->vendor = htonl(NX_VENDOR_ID);
-    nxh->subtype = htonl(subtype);
-    return nxh;
+    *bufferp = ofpbuf_new(openflow_len);
+    return put_nxmsg_xid(openflow_len, subtype, xid, *bufferp);
 }
 
 /* Appends 'openflow_len' bytes to 'buffer', starting with an OpenFlow header
@@ -870,6 +1328,28 @@ put_openflow_xid(size_t openflow_len, uint8_t type, ovs_be32 xid,
     return oh;
 }
 
+/* Similar to put_openflow() but append a Nicira vendor extension message with
+ * the specific 'subtype'.  'subtype' should be in host byte order. */
+void *
+put_nxmsg(size_t openflow_len, uint32_t subtype, struct ofpbuf *buffer)
+{
+    return put_nxmsg_xid(openflow_len, subtype, alloc_xid(), buffer);
+}
+
+/* Similar to put_openflow_xid() but append a Nicira vendor extension message
+ * with the specific 'subtype'.  'subtype' should be in host byte order. */
+void *
+put_nxmsg_xid(size_t openflow_len, uint32_t subtype, ovs_be32 xid,
+              struct ofpbuf *buffer)
+{
+    struct nicira_header *nxh;
+
+    nxh = put_openflow_xid(openflow_len, OFPT_VENDOR, xid, buffer);
+    nxh->vendor = htonl(NX_VENDOR_ID);
+    nxh->subtype = htonl(subtype);
+    return nxh;
+}
+
 /* Updates the 'length' field of the OpenFlow message in 'buffer' to
  * 'buffer->size'. */
 void
@@ -879,6 +1359,37 @@ update_openflow_length(struct ofpbuf *buffer)
     oh->length = htons(buffer->size);
 }
 
+/* Creates an ofp_stats_request with the given 'type' and 'body_len' bytes of
+ * space allocated for the 'body' member.  Returns the first byte of the 'body'
+ * member. */
+void *
+ofputil_make_stats_request(size_t body_len, uint16_t type,
+                           struct ofpbuf **bufferp)
+{
+    struct ofp_stats_request *osr;
+    osr = make_openflow((offsetof(struct ofp_stats_request, body)
+                        + body_len), OFPT_STATS_REQUEST, bufferp);
+    osr->type = htons(type);
+    osr->flags = htons(0);
+    return osr->body;
+}
+
+/* Creates a stats request message with Nicira as vendor and the given
+ * 'subtype', of total length 'openflow_len'.  Returns the message. */
+void *
+ofputil_make_nxstats_request(size_t openflow_len, uint32_t subtype,
+                             struct ofpbuf **bufferp)
+{
+    struct nicira_stats_msg *nsm;
+
+    nsm = make_openflow(openflow_len, OFPT_STATS_REQUEST, bufferp);
+    nsm->type = htons(OFPST_VENDOR);
+    nsm->flags = htons(0);
+    nsm->vendor = htonl(NX_VENDOR_ID);
+    nsm->subtype = htonl(subtype);
+    return nsm;
+}
+
 /* Returns the first byte of the 'body' member of the ofp_stats_request or
  * ofp_stats_reply in 'oh'. */
 const void *
@@ -897,6 +1408,22 @@ ofputil_stats_body_len(const struct ofp_header *oh)
     return ntohs(oh->length) - sizeof(struct ofp_stats_request);
 }
 
+/* Returns the first byte of the body of the nicira_stats_msg in 'oh'. */
+const void *
+ofputil_nxstats_body(const struct ofp_header *oh)
+{
+    assert(oh->type == OFPT_STATS_REQUEST || oh->type == OFPT_STATS_REPLY);
+    return ((const struct nicira_stats_msg *) oh) + 1;
+}
+
+/* Returns the length of the body of the nicira_stats_msg in 'oh'. */
+size_t
+ofputil_nxstats_body_len(const struct ofp_header *oh)
+{
+    assert(oh->type == OFPT_STATS_REQUEST || oh->type == OFPT_STATS_REPLY);
+    return ntohs(oh->length) - sizeof(struct nicira_stats_msg);
+}
+
 struct ofpbuf *
 make_flow_mod(uint16_t command, const struct cls_rule *rule,
               size_t actions_len)
@@ -910,7 +1437,7 @@ make_flow_mod(uint16_t command, const struct cls_rule *rule,
     ofm->header.length = htons(size);
     ofm->cookie = 0;
     ofm->priority = htons(MIN(rule->priority, UINT16_MAX));
-    ofputil_cls_rule_to_match(rule, NXFF_OPENFLOW10, &ofm->match);
+    ofputil_cls_rule_to_match(rule, NXFF_OPENFLOW10, &ofm->match, 0, NULL);
     ofm->command = htons(command);
     return out;
 }
@@ -1170,6 +1697,7 @@ check_nicira_action(const union ofp_action *a, unsigned int len,
                     const struct flow *flow)
 {
     const struct nx_action_header *nah;
+    uint16_t subtype;
     int error;
 
     if (len < 16) {
@@ -1179,7 +1707,14 @@ check_nicira_action(const union ofp_action *a, unsigned int len,
     }
     nah = (const struct nx_action_header *) a;
 
-    switch (ntohs(nah->subtype)) {
+    subtype = ntohs(nah->subtype);
+    if (subtype > TYPE_MAXIMUM(enum nx_action_subtype)) {
+        /* This is necessary because enum nx_action_subtype is probably an
+         * 8-bit type, so the cast below throws away the top 8 bits. */
+        return ofp_mkerr(OFPET_BAD_ACTION, OFPBAC_BAD_VENDOR_TYPE);
+    }
+
+    switch ((enum nx_action_subtype) subtype) {
     case NXAST_RESUBMIT:
     case NXAST_SET_TUNNEL:
     case NXAST_DROP_SPOOFED_ARP:
@@ -1206,6 +1741,11 @@ check_nicira_action(const union ofp_action *a, unsigned int len,
     case NXAST_NOTE:
         return 0;
 
+    case NXAST_SET_TUNNEL64:
+        return check_action_exact_len(a, len,
+                                      sizeof(struct nx_action_set_tunnel64));
+
+    case NXAST_SNAT__OBSOLETE:
     default:
         return ofp_mkerr(OFPET_BAD_ACTION, OFPBAC_BAD_VENDOR_TYPE);
     }
@@ -1215,9 +1755,10 @@ static int
 check_action(const union ofp_action *a, unsigned int len,
              const struct flow *flow, int max_ports)
 {
+    enum ofp_action_type type = ntohs(a->type);
     int error;
 
-    switch (ntohs(a->type)) {
+    switch (type) {
     case OFPAT_OUTPUT:
         error = check_action_exact_len(a, len, 8);
         if (error) {
@@ -1266,8 +1807,7 @@ check_action(const union ofp_action *a, unsigned int len,
         return check_enqueue_action(a, len, max_ports);
 
     default:
-        VLOG_WARN_RL(&bad_ofmsg_rl, "unknown action type %"PRIu16,
-                ntohs(a->type));
+        VLOG_WARN_RL(&bad_ofmsg_rl, "unknown action type %d", (int) type);
         return ofp_mkerr(OFPET_BAD_ACTION, OFPBAC_BAD_TYPE);
     }
 }
@@ -1542,13 +2082,14 @@ make_ofp_error_msg(int error, const struct ofp_header *oh)
         oem->type = htons(NXET_VENDOR);
         oem->code = htons(NXVC_VENDOR_ERROR);
 
-        nve = ofpbuf_put_uninit(buf, sizeof *nve);
+        nve = (struct nx_vendor_error *)oem->data;
         nve->vendor = htonl(vendor_id);
         nve->type = htons(type);
         nve->code = htons(code);
     }
 
     if (len) {
+        buf->size -= len;
         ofpbuf_put(buf, data, len);
     }