Support userdata in NXT_PACKET_IN2.
[cascardo/ovs.git] / lib / ofp-util.c
index 1e3d5b2..b220112 100644 (file)
@@ -41,6 +41,7 @@
 #include "ofpbuf.h"
 #include "openflow/netronome-ext.h"
 #include "packets.h"
+#include "pktbuf.h"
 #include "random.h"
 #include "tun-metadata.h"
 #include "unaligned.h"
@@ -1124,8 +1125,9 @@ 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:
+    case NXPIF_STANDARD:
+    case NXPIF_NXT_PACKET_IN:
+    case NXPIF_NXT_PACKET_IN2:
         return true;
     }
 
@@ -1136,10 +1138,12 @@ 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";
+    case NXPIF_STANDARD:
+        return "standard";
+    case NXPIF_NXT_PACKET_IN:
+        return "nxt_packet_in";
+    case NXPIF_NXT_PACKET_IN2:
+        return "nxt_packet_in2";
     default:
         OVS_NOT_REACHED();
     }
@@ -1148,8 +1152,12 @@ ofputil_packet_in_format_to_string(enum nx_packet_in_format packet_in_format)
 int
 ofputil_packet_in_format_from_string(const char *s)
 {
-    return (!strcmp(s, "openflow10") ? NXPIF_OPENFLOW10
-            : !strcmp(s, "nxm") ? NXPIF_NXM
+    return (!strcmp(s, "standard") || !strcmp(s, "openflow10")
+            ? NXPIF_STANDARD
+            : !strcmp(s, "nxt_packet_in") || !strcmp(s, "nxm")
+            ? NXPIF_NXT_PACKET_IN
+            : !strcmp(s, "nxt_packet_in2")
+            ? NXPIF_NXT_PACKET_IN2
             : -1);
 }
 
@@ -1243,13 +1251,12 @@ version_bitmap_from_version(uint8_t ofp_version)
 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));
+    struct ofpbuf msg = ofpbuf_const_initializer(oh, ntohs(oh->length));
     ofpbuf_pull(&msg, sizeof *oh);
 
     *allowed_versions = version_bitmap_from_version(oh->version);
+
+    bool ok = true;
     while (msg.size) {
         const struct ofp_hello_elem_header *oheh;
         unsigned int len;
@@ -1544,14 +1551,12 @@ ofputil_decode_flow_mod(struct ofputil_flow_mod *fm,
 {
     ovs_be16 raw_flags;
     enum ofperr error;
-    struct ofpbuf b;
-    enum ofpraw raw;
 
     /* Ignored for non-delete actions */
     fm->delete_reason = OFPRR_DELETE;
 
-    ofpbuf_use_const(&b, oh, ntohs(oh->length));
-    raw = ofpraw_pull_assert(&b);
+    struct ofpbuf b = ofpbuf_const_initializer(oh, ntohs(oh->length));
+    enum ofpraw raw = ofpraw_pull_assert(&b);
     if (raw == OFPRAW_OFPT11_FLOW_MOD) {
         /* Standard OpenFlow 1.1+ flow_mod. */
         const struct ofp11_flow_mod *ofm;
@@ -1772,12 +1777,9 @@ 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));
+    struct ofpbuf b = ofpbuf_const_initializer(oh, ntohs(oh->length));
     ofpraw_pull_assert(&b);
-    omm = ofpbuf_pull(&b, sizeof *omm);
+    const struct ofp13_meter_mod *omm = ofpbuf_pull(&b, sizeof *omm);
 
     /* Translate the message. */
     mm->command = ntohs(omm->command);
@@ -2341,11 +2343,8 @@ ofputil_decode_queue_get_config_request(const struct ofp_header *oh,
     const struct ofp10_queue_get_config_request *qgcr10;
     const struct ofp11_queue_get_config_request *qgcr11;
     const struct ofp14_queue_desc_request *qdr14;
-    enum ofpraw raw;
-    struct ofpbuf b;
-
-    ofpbuf_use_const(&b, oh, ntohs(oh->length));
-    raw = ofpraw_pull_assert(&b);
+    struct ofpbuf b = ofpbuf_const_initializer(oh, ntohs(oh->length));
+    enum ofpraw raw = ofpraw_pull_assert(&b);
 
     switch ((int) raw) {
     case OFPRAW_OFPT10_QUEUE_GET_CONFIG_REQUEST:
@@ -2613,8 +2612,8 @@ ofputil_pull_queue_get_config_reply14(struct ofpbuf *msg,
     }
     len -= sizeof *oqd14;
 
-    struct ofpbuf properties;
-    ofpbuf_use_const(&properties, ofpbuf_pull(msg, len), len);
+    struct ofpbuf properties = ofpbuf_const_initializer(ofpbuf_pull(msg, len),
+                                                        len);
     while (properties.size > 0) {
         struct ofpbuf payload;
         uint64_t type;
@@ -2694,11 +2693,8 @@ 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);
+    struct ofpbuf b = ofpbuf_const_initializer(oh, ntohs(oh->length));
+    enum ofpraw raw = ofpraw_pull_assert(&b);
     switch ((int) raw) {
     case OFPRAW_OFPST10_FLOW_REQUEST:
         return ofputil_decode_ofpst10_flow_request(fsr, b.data, false);
@@ -3126,13 +3122,10 @@ enum ofperr
 ofputil_decode_aggregate_stats_reply(struct ofputil_aggregate_stats *stats,
                                      const struct ofp_header *reply)
 {
-    struct ofp_aggregate_stats_reply *asr;
-    struct ofpbuf msg;
-
-    ofpbuf_use_const(&msg, reply, ntohs(reply->length));
+    struct ofpbuf msg = ofpbuf_const_initializer(reply, ntohs(reply->length));
     ofpraw_pull_assert(&msg);
 
-    asr = msg.msg;
+    struct ofp_aggregate_stats_reply *asr = msg.msg;
     stats->packet_count = ntohll(get_32aligned_be64(&asr->packet_count));
     stats->byte_count = ntohll(get_32aligned_be64(&asr->byte_count));
     stats->flow_count = ntohl(asr->flow_count);
@@ -3147,11 +3140,8 @@ enum ofperr
 ofputil_decode_flow_removed(struct ofputil_flow_removed *fr,
                             const struct ofp_header *oh)
 {
-    enum ofpraw raw;
-    struct ofpbuf b;
-
-    ofpbuf_use_const(&b, oh, ntohs(oh->length));
-    raw = ofpraw_pull_assert(&b);
+    struct ofpbuf b = ofpbuf_const_initializer(oh, ntohs(oh->length));
+    enum ofpraw raw = ofpraw_pull_assert(&b);
     if (raw == OFPRAW_OFPT11_FLOW_REMOVED) {
         const struct ofp12_flow_removed *ofr;
         enum ofperr error;
@@ -3311,31 +3301,117 @@ ofputil_encode_flow_removed(const struct ofputil_flow_removed *fr,
     return msg;
 }
 
-enum ofperr
-ofputil_decode_packet_in(struct ofputil_packet_in *pin,
-                         const struct ofp_header *oh)
+/* The caller has done basic initialization of '*pin'; the other output
+ * arguments needs to be initialized. */
+static enum ofperr
+decode_nx_packet_in2(const struct ofp_header *oh,
+                     struct ofputil_packet_in *pin,
+                     size_t *total_len, uint32_t *buffer_id)
 {
-    enum ofpraw raw;
-    struct ofpbuf b;
+    *total_len = 0;
+    *buffer_id = UINT32_MAX;
 
-    memset(pin, 0, sizeof *pin);
-    pin->cookie = OVS_BE64_MAX;
+    struct ofpbuf properties;
+    ofpbuf_use_const(&properties, oh, ntohs(oh->length));
+    ofpraw_pull_assert(&properties);
 
-    ofpbuf_use_const(&b, oh, ntohs(oh->length));
-    raw = ofpraw_pull_assert(&b);
-    if (raw == OFPRAW_OFPT13_PACKET_IN || raw == OFPRAW_OFPT12_PACKET_IN) {
-        const struct ofp13_packet_in *opi;
-        int error;
-        size_t packet_in_size;
+    while (properties.size > 0) {
+        struct ofpbuf payload;
+        uint64_t type;
 
-        if (raw == OFPRAW_OFPT12_PACKET_IN) {
-            packet_in_size = sizeof (struct ofp12_packet_in);
-        } else {
-            packet_in_size = sizeof (struct ofp13_packet_in);
+        enum ofperr error = ofpprop_pull(&properties, &payload, &type);
+        if (error) {
+            return error;
+        }
+
+        switch (type) {
+        case NXPINT_PACKET:
+            pin->packet = payload.msg;
+            pin->packet_len = ofpbuf_msgsize(&payload);
+            break;
+
+        case NXPINT_FULL_LEN: {
+            uint32_t u32;
+            error = ofpprop_parse_u32(&payload, &u32);
+            *total_len = u32;
+            break;
         }
 
-        opi = ofpbuf_pull(&b, packet_in_size);
-        error = oxm_pull_match_loose(&b, &pin->flow_metadata);
+        case NXPINT_BUFFER_ID:
+            error = ofpprop_parse_u32(&payload, buffer_id);
+            break;
+
+        case NXPINT_TABLE_ID:
+            error = ofpprop_parse_u8(&payload, &pin->table_id);
+            break;
+
+        case NXPINT_COOKIE:
+            error = ofpprop_parse_be64(&payload, &pin->cookie);
+            break;
+
+        case NXPINT_REASON: {
+            uint8_t reason;
+            error = ofpprop_parse_u8(&payload, &reason);
+            pin->reason = reason;
+            break;
+        }
+
+        case NXPINT_METADATA:
+            error = oxm_decode_match(payload.msg, ofpbuf_msgsize(&payload),
+                                     &pin->flow_metadata);
+            break;
+
+        case NXPINT_USERDATA:
+            pin->userdata = payload.msg;
+            pin->userdata_len = ofpbuf_msgsize(&payload);
+            break;
+
+        default:
+            error = OFPPROP_UNKNOWN(true, "NX_PACKET_IN2", type);
+            break;
+        }
+        if (error) {
+            return error;
+        }
+    }
+
+    if (!pin->packet_len) {
+        VLOG_WARN_RL(&bad_ofmsg_rl, "NXT_PACKET_IN2 lacks packet");
+        return OFPERR_OFPBRC_BAD_LEN;
+    } else if (!*total_len) {
+        *total_len = pin->packet_len;
+    } else if (*total_len < pin->packet_len) {
+        VLOG_WARN_RL(&bad_ofmsg_rl, "NXT_PACKET_IN2 claimed full_len < len");
+        return OFPERR_OFPBRC_BAD_LEN;
+    }
+
+    return 0;
+}
+
+/* Decodes the packet-in message starting at 'oh' into '*pin'.  Populates
+ * 'pin->packet' and 'pin->len' with the part of the packet actually included
+ * in the message, and '*total_len' with the original length of the packet
+ * (which is larger than 'packet->len' if only part of the packet was
+ * included).  Stores the packet's buffer ID in '*buffer_id' (UINT32_MAX if it
+ * was not buffered).
+ *
+ * Returns 0 if successful, otherwise an OpenFlow error code. */
+enum ofperr
+ofputil_decode_packet_in(const struct ofp_header *oh,
+                         struct ofputil_packet_in *pin,
+                         size_t *total_len, uint32_t *buffer_id)
+{
+    memset(pin, 0, sizeof *pin);
+    pin->cookie = OVS_BE64_MAX;
+
+    struct ofpbuf b = ofpbuf_const_initializer(oh, ntohs(oh->length));
+    enum ofpraw raw = ofpraw_pull_assert(&b);
+    if (raw == OFPRAW_OFPT13_PACKET_IN || raw == OFPRAW_OFPT12_PACKET_IN) {
+        const struct ofp12_packet_in *opi = ofpbuf_pull(&b, sizeof *opi);
+        const ovs_be64 *cookie = (raw == OFPRAW_OFPT13_PACKET_IN
+                                  ? ofpbuf_pull(&b, sizeof *cookie)
+                                  : NULL);
+        enum ofperr error = oxm_pull_match_loose(&b, &pin->flow_metadata);
         if (error) {
             return error;
         }
@@ -3344,13 +3420,12 @@ ofputil_decode_packet_in(struct ofputil_packet_in *pin,
             return OFPERR_OFPBRC_BAD_LEN;
         }
 
-        pin->reason = opi->pi.reason;
-        pin->table_id = opi->pi.table_id;
-        pin->buffer_id = ntohl(opi->pi.buffer_id);
-        pin->total_len = ntohs(opi->pi.total_len);
-
-        if (raw == OFPRAW_OFPT13_PACKET_IN) {
-            pin->cookie = opi->cookie;
+        pin->reason = opi->reason;
+        pin->table_id = opi->table_id;
+        *buffer_id = ntohl(opi->buffer_id);
+        *total_len = ntohs(opi->total_len);
+        if (cookie) {
+            pin->cookie = *cookie;
         }
 
         pin->packet = b.data;
@@ -3360,14 +3435,15 @@ ofputil_decode_packet_in(struct ofputil_packet_in *pin,
 
         opi = ofpbuf_pull(&b, offsetof(struct ofp10_packet_in, data));
 
-        pin->packet = opi->data;
+        pin->packet = CONST_CAST(uint8_t *, opi->data);
         pin->packet_len = b.size;
 
         match_init_catchall(&pin->flow_metadata);
-        match_set_in_port(&pin->flow_metadata, u16_to_ofp(ntohs(opi->in_port)));
+        match_set_in_port(&pin->flow_metadata,
+                          u16_to_ofp(ntohs(opi->in_port)));
         pin->reason = opi->reason;
-        pin->buffer_id = ntohl(opi->buffer_id);
-        pin->total_len = ntohs(opi->total_len);
+        *buffer_id = ntohl(opi->buffer_id);
+        *total_len = ntohs(opi->total_len);
     } else if (raw == OFPRAW_OFPT11_PACKET_IN) {
         const struct ofp11_packet_in *opi;
         ofp_port_t in_port;
@@ -3378,14 +3454,14 @@ ofputil_decode_packet_in(struct ofputil_packet_in *pin,
         pin->packet = b.data;
         pin->packet_len = b.size;
 
-        pin->buffer_id = ntohl(opi->buffer_id);
+        *buffer_id = ntohl(opi->buffer_id);
         error = ofputil_port_from_ofp11(opi->in_port, &in_port);
         if (error) {
             return error;
         }
         match_init_catchall(&pin->flow_metadata);
         match_set_in_port(&pin->flow_metadata, in_port);
-        pin->total_len = ntohs(opi->total_len);
+        *total_len = ntohs(opi->total_len);
         pin->reason = opi->reason;
         pin->table_id = opi->table_id;
     } else if (raw == OFPRAW_NXT_PACKET_IN) {
@@ -3407,11 +3483,13 @@ ofputil_decode_packet_in(struct ofputil_packet_in *pin,
         pin->table_id = npi->table_id;
         pin->cookie = npi->cookie;
 
-        pin->buffer_id = ntohl(npi->buffer_id);
-        pin->total_len = ntohs(npi->total_len);
+        *buffer_id = ntohl(npi->buffer_id);
+        *total_len = ntohs(npi->total_len);
 
         pin->packet = b.data;
         pin->packet_len = b.size;
+    } else if (raw == OFPRAW_NXT_PACKET_IN2) {
+        return decode_nx_packet_in2(oh, pin, total_len, buffer_id);
     } else {
         OVS_NOT_REACHED();
     }
@@ -3419,149 +3497,244 @@ ofputil_decode_packet_in(struct ofputil_packet_in *pin,
     return 0;
 }
 
+static int
+encode_packet_in_reason(enum ofp_packet_in_reason reason,
+                        enum ofp_version version)
+{
+    switch (reason) {
+    case OFPR_NO_MATCH:
+    case OFPR_ACTION:
+    case OFPR_INVALID_TTL:
+        return reason;
+
+    case OFPR_ACTION_SET:
+    case OFPR_GROUP:
+    case OFPR_PACKET_OUT:
+        return version < OFP14_VERSION ? OFPR_ACTION : reason;
+
+    case OFPR_EXPLICIT_MISS:
+        return version < OFP13_VERSION ? OFPR_ACTION : OFPR_NO_MATCH;
+
+    case OFPR_IMPLICIT_MISS:
+        return OFPR_NO_MATCH;
+
+    case OFPR_N_REASONS:
+    default:
+        OVS_NOT_REACHED();
+    }
+}
+
 static struct ofpbuf *
-ofputil_encode_ofp10_packet_in(const struct ofputil_packet_in *pin)
+ofputil_encode_ofp10_packet_in(const struct ofputil_packet_in *pin,
+                               uint32_t buffer_id)
 {
     struct ofp10_packet_in *opi;
-    struct ofpbuf *packet;
+    struct ofpbuf *msg;
 
-    packet = ofpraw_alloc_xid(OFPRAW_OFPT10_PACKET_IN, OFP10_VERSION,
-                              htonl(0), pin->packet_len);
-    opi = ofpbuf_put_zeros(packet, offsetof(struct ofp10_packet_in, data));
-    opi->total_len = htons(pin->total_len);
+    msg = ofpraw_alloc_xid(OFPRAW_OFPT10_PACKET_IN, OFP10_VERSION,
+                           htonl(0), pin->packet_len);
+    opi = ofpbuf_put_zeros(msg, offsetof(struct ofp10_packet_in, data));
+    opi->total_len = htons(pin->packet_len);
     opi->in_port = htons(ofp_to_u16(pin->flow_metadata.flow.in_port.ofp_port));
-    opi->reason = pin->reason;
-    opi->buffer_id = htonl(pin->buffer_id);
+    opi->reason = encode_packet_in_reason(pin->reason, OFP10_VERSION);
+    opi->buffer_id = htonl(buffer_id);
 
-    ofpbuf_put(packet, pin->packet, pin->packet_len);
-
-    return packet;
+    return msg;
 }
 
 static struct ofpbuf *
-ofputil_encode_nx_packet_in(const struct ofputil_packet_in *pin)
+ofputil_encode_nx_packet_in(const struct ofputil_packet_in *pin,
+                            enum ofp_version version, uint32_t buffer_id)
 {
     struct nx_packet_in *npi;
-    struct ofpbuf *packet;
+    struct ofpbuf *msg;
     size_t match_len;
 
     /* The final argument is just an estimate of the space required. */
-    packet = ofpraw_alloc_xid(OFPRAW_NXT_PACKET_IN, OFP10_VERSION,
-                              htonl(0), NXM_TYPICAL_LEN + 2 + pin->packet_len);
-    ofpbuf_put_zeros(packet, sizeof *npi);
-    match_len = nx_put_match(packet, &pin->flow_metadata, 0, 0);
-    ofpbuf_put_zeros(packet, 2);
-    ofpbuf_put(packet, pin->packet, pin->packet_len);
-
-    npi = packet->msg;
-    npi->buffer_id = htonl(pin->buffer_id);
-    npi->total_len = htons(pin->total_len);
-    npi->reason = pin->reason;
+    msg = ofpraw_alloc_xid(OFPRAW_NXT_PACKET_IN, version,
+                           htonl(0), NXM_TYPICAL_LEN + 2 + pin->packet_len);
+    ofpbuf_put_zeros(msg, sizeof *npi);
+    match_len = nx_put_match(msg, &pin->flow_metadata, 0, 0);
+    ofpbuf_put_zeros(msg, 2);
+
+    npi = msg->msg;
+    npi->buffer_id = htonl(buffer_id);
+    npi->total_len = htons(pin->packet_len);
+    npi->reason = encode_packet_in_reason(pin->reason, version);
     npi->table_id = pin->table_id;
     npi->cookie = pin->cookie;
     npi->match_len = htons(match_len);
 
-    return packet;
+    return msg;
 }
 
 static struct ofpbuf *
-ofputil_encode_ofp11_packet_in(const struct ofputil_packet_in *pin)
+ofputil_encode_nx_packet_in2(const struct ofputil_packet_in *pin,
+                             enum ofp_version version, uint32_t buffer_id,
+                             size_t include_bytes)
+{
+    /* 'extra' is just an estimate of the space required. */
+    size_t extra = include_bytes + NXM_TYPICAL_LEN + 256;
+    struct ofpbuf *msg = ofpraw_alloc_xid(OFPRAW_NXT_PACKET_IN2, version,
+                                          htonl(0), extra);
+
+    /* Add packet properties. */
+    ofpprop_put(msg, NXPINT_PACKET, pin->packet, include_bytes);
+    if (include_bytes != pin->packet_len) {
+        ofpprop_put_u32(msg, NXPINT_FULL_LEN, pin->packet_len);
+    }
+    if (buffer_id != UINT32_MAX) {
+        ofpprop_put_u32(msg, NXPINT_BUFFER_ID, buffer_id);
+    }
+
+    /* Add flow properties. */
+    ofpprop_put_u8(msg, NXPINT_TABLE_ID, pin->table_id);
+    if (pin->cookie != OVS_BE64_MAX) {
+        ofpprop_put_be64(msg, NXPINT_COOKIE, pin->cookie);
+    }
+
+    /* Add other properties. */
+    ofpprop_put_u8(msg, NXPINT_REASON,
+                   encode_packet_in_reason(pin->reason, version));
+
+    size_t start = ofpprop_start(msg, NXPINT_METADATA);
+    oxm_put_raw(msg, &pin->flow_metadata, version);
+    ofpprop_end(msg, start);
+
+    if (pin->userdata_len) {
+        ofpprop_put(msg, NXPINT_USERDATA, pin->userdata, pin->userdata_len);
+    }
+
+    ofpmsg_update_length(msg);
+    return msg;
+}
+
+static struct ofpbuf *
+ofputil_encode_ofp11_packet_in(const struct ofputil_packet_in *pin,
+                               uint32_t buffer_id)
 {
     struct ofp11_packet_in *opi;
-    struct ofpbuf *packet;
+    struct ofpbuf *msg;
 
-    packet = ofpraw_alloc_xid(OFPRAW_OFPT11_PACKET_IN, OFP11_VERSION,
-                              htonl(0), pin->packet_len);
-    opi = ofpbuf_put_zeros(packet, sizeof *opi);
-    opi->buffer_id = htonl(pin->buffer_id);
-    opi->in_port = ofputil_port_to_ofp11(pin->flow_metadata.flow.in_port.ofp_port);
+    msg = ofpraw_alloc_xid(OFPRAW_OFPT11_PACKET_IN, OFP11_VERSION,
+                           htonl(0), pin->packet_len);
+    opi = ofpbuf_put_zeros(msg, sizeof *opi);
+    opi->buffer_id = htonl(buffer_id);
+    opi->in_port = ofputil_port_to_ofp11(
+        pin->flow_metadata.flow.in_port.ofp_port);
     opi->in_phy_port = opi->in_port;
-    opi->total_len = htons(pin->total_len);
-    opi->reason = pin->reason;
+    opi->total_len = htons(pin->packet_len);
+    opi->reason = encode_packet_in_reason(pin->reason, OFP11_VERSION);
     opi->table_id = pin->table_id;
 
-    ofpbuf_put(packet, pin->packet, pin->packet_len);
-
-    return packet;
+    return msg;
 }
 
 static struct ofpbuf *
 ofputil_encode_ofp12_packet_in(const struct ofputil_packet_in *pin,
-                               enum ofputil_protocol protocol)
-{
-    struct ofp13_packet_in *opi;
-    enum ofpraw packet_in_raw;
-    enum ofp_version packet_in_version;
-    size_t packet_in_size;
-    struct ofpbuf *packet;
-
-    if (protocol == OFPUTIL_P_OF12_OXM) {
-        packet_in_raw = OFPRAW_OFPT12_PACKET_IN;
-        packet_in_version = OFP12_VERSION;
-        packet_in_size = sizeof (struct ofp12_packet_in);
-    } else {
-        packet_in_raw = OFPRAW_OFPT13_PACKET_IN;
-        packet_in_version = ofputil_protocol_to_ofp_version(protocol);
-        packet_in_size = sizeof (struct ofp13_packet_in);
-    }
+                               enum ofp_version version,
+                               uint32_t buffer_id)
+{
+    enum ofpraw raw = (version >= OFP13_VERSION
+                       ? OFPRAW_OFPT13_PACKET_IN
+                       : OFPRAW_OFPT12_PACKET_IN);
+    struct ofpbuf *msg;
 
     /* The final argument is just an estimate of the space required. */
-    packet = ofpraw_alloc_xid(packet_in_raw, packet_in_version,
-                              htonl(0), NXM_TYPICAL_LEN + 2 + pin->packet_len);
-    ofpbuf_put_zeros(packet, packet_in_size);
-    oxm_put_match(packet, &pin->flow_metadata,
-                  ofputil_protocol_to_ofp_version(protocol));
-    ofpbuf_put_zeros(packet, 2);
-    ofpbuf_put(packet, pin->packet, pin->packet_len);
+    msg = ofpraw_alloc_xid(raw, version,
+                           htonl(0), NXM_TYPICAL_LEN + 2 + pin->packet_len);
+
+    struct ofp12_packet_in *opi = ofpbuf_put_zeros(msg, sizeof *opi);
+    opi->buffer_id = htonl(buffer_id);
+    opi->total_len = htons(pin->packet_len);
+    opi->reason = encode_packet_in_reason(pin->reason, version);
+    opi->table_id = pin->table_id;
 
-    opi = packet->msg;
-    opi->pi.buffer_id = htonl(pin->buffer_id);
-    opi->pi.total_len = htons(pin->total_len);
-    opi->pi.reason = pin->reason;
-    opi->pi.table_id = pin->table_id;
-    if (protocol != OFPUTIL_P_OF12_OXM) {
-        opi->cookie = pin->cookie;
+    if (version >= OFP13_VERSION) {
+        ovs_be64 cookie = pin->cookie;
+        ofpbuf_put(msg, &cookie, sizeof cookie);
     }
 
-    return packet;
+    oxm_put_match(msg, &pin->flow_metadata, version);
+    ofpbuf_put_zeros(msg, 2);
+
+    return msg;
 }
 
-/* Converts abstract ofputil_packet_in 'pin' into a PACKET_IN message
- * in the format specified by 'packet_in_format'.  */
+/* Converts abstract ofputil_packet_in 'pin' into a PACKET_IN message for
+ * 'protocol', using the packet-in format specified by 'packet_in_format'.
+ *
+ * If 'pkt_buf' is nonnull and 'max_len' allows the packet to be buffered, this
+ * function will attempt to obtain a buffer ID from 'pktbuf' and truncate the
+ * packet to 'max_len' bytes.  Otherwise, or if 'pktbuf' doesn't have a free
+ * buffer, it will send the whole packet without buffering. */
 struct ofpbuf *
 ofputil_encode_packet_in(const struct ofputil_packet_in *pin,
                          enum ofputil_protocol protocol,
-                         enum nx_packet_in_format packet_in_format)
+                         enum nx_packet_in_format packet_in_format,
+                         uint16_t max_len, struct pktbuf *pktbuf)
 {
-    struct ofpbuf *packet;
+    enum ofp_version version = ofputil_protocol_to_ofp_version(protocol);
 
-    switch (protocol) {
-    case OFPUTIL_P_OF10_STD:
-    case OFPUTIL_P_OF10_STD_TID:
-    case OFPUTIL_P_OF10_NXM:
-    case OFPUTIL_P_OF10_NXM_TID:
-        packet = (packet_in_format == NXPIF_NXM
-                  ? ofputil_encode_nx_packet_in(pin)
-                  : ofputil_encode_ofp10_packet_in(pin));
-        break;
+    /* Get buffer ID. */
+    ofp_port_t in_port = pin->flow_metadata.flow.in_port.ofp_port;
+    uint32_t buffer_id = (max_len != OFPCML12_NO_BUFFER && pktbuf
+                          ? pktbuf_save(pktbuf, pin->packet,
+                                        pin->packet_len, in_port)
+                          : UINT32_MAX);
 
-    case OFPUTIL_P_OF11_STD:
-        packet = ofputil_encode_ofp11_packet_in(pin);
+    /* Calculate the number of bytes of the packet to include in the
+     * packet-in:
+     *
+     *    - If not buffered, the whole thing.
+     *
+     *    - Otherwise, no more than 'max_len' bytes. */
+    size_t include_bytes = (buffer_id == UINT32_MAX
+                            ? pin->packet_len
+                            : MIN(max_len, pin->packet_len));
+
+    struct ofpbuf *msg;
+    switch (packet_in_format) {
+    case NXPIF_STANDARD:
+        switch (protocol) {
+        case OFPUTIL_P_OF10_STD:
+        case OFPUTIL_P_OF10_STD_TID:
+        case OFPUTIL_P_OF10_NXM:
+        case OFPUTIL_P_OF10_NXM_TID:
+            msg = ofputil_encode_ofp10_packet_in(pin, buffer_id);
+            break;
+
+        case OFPUTIL_P_OF11_STD:
+            msg = ofputil_encode_ofp11_packet_in(pin, buffer_id);
+            break;
+
+        case OFPUTIL_P_OF12_OXM:
+        case OFPUTIL_P_OF13_OXM:
+        case OFPUTIL_P_OF14_OXM:
+        case OFPUTIL_P_OF15_OXM:
+            msg = ofputil_encode_ofp12_packet_in(pin, version, buffer_id);
+            break;
+
+        default:
+            OVS_NOT_REACHED();
+        }
         break;
 
-    case OFPUTIL_P_OF12_OXM:
-    case OFPUTIL_P_OF13_OXM:
-    case OFPUTIL_P_OF14_OXM:
-    case OFPUTIL_P_OF15_OXM:
-        packet = ofputil_encode_ofp12_packet_in(pin, protocol);
+    case NXPIF_NXT_PACKET_IN:
+        msg = ofputil_encode_nx_packet_in(pin, version, buffer_id);
         break;
 
+    case NXPIF_NXT_PACKET_IN2:
+        return ofputil_encode_nx_packet_in2(pin, version, buffer_id,
+                                            include_bytes);
+
     default:
         OVS_NOT_REACHED();
     }
 
-    ofpmsg_update_length(packet);
-    return packet;
+    ofpbuf_put(msg, pin->packet, include_bytes);
+    ofpmsg_update_length(msg);
+    return msg;
 }
 
 /* Returns a string form of 'reason'.  The return value is either a statically
@@ -3584,6 +3757,9 @@ ofputil_packet_in_reason_to_string(enum ofp_packet_in_reason reason,
         return "group";
     case OFPR_PACKET_OUT:
         return "packet_out";
+    case OFPR_EXPLICIT_MISS:
+    case OFPR_IMPLICIT_MISS:
+        return "";
 
     case OFPR_N_REASONS:
     default:
@@ -3625,11 +3801,8 @@ ofputil_decode_packet_out(struct ofputil_packet_out *po,
                           const struct ofp_header *oh,
                           struct ofpbuf *ofpacts)
 {
-    enum ofpraw raw;
-    struct ofpbuf b;
-
-    ofpbuf_use_const(&b, oh, ntohs(oh->length));
-    raw = ofpraw_pull_assert(&b);
+    struct ofpbuf b = ofpbuf_const_initializer(oh, ntohs(oh->length));
+    enum ofpraw raw = ofpraw_pull_assert(&b);
 
     ofpbuf_clear(ofpacts);
     if (raw == OFPRAW_OFPT11_PACKET_OUT) {
@@ -3818,24 +3991,18 @@ parse_ofp14_port_ethernet_property(const struct ofpbuf *payload,
 static enum ofperr
 ofputil_pull_ofp14_port(struct ofputil_phy_port *pp, struct ofpbuf *msg)
 {
-    struct ofpbuf properties;
-    struct ofp14_port *op;
-    enum ofperr error;
-    size_t len;
-
-    op = ofpbuf_try_pull(msg, sizeof *op);
+    struct ofp14_port *op = ofpbuf_try_pull(msg, sizeof *op);
     if (!op) {
         return OFPERR_OFPBRC_BAD_LEN;
     }
 
-    len = ntohs(op->length);
+    size_t len = ntohs(op->length);
     if (len < sizeof *op || len - sizeof *op > msg->size) {
         return OFPERR_OFPBRC_BAD_LEN;
     }
     len -= sizeof *op;
-    ofpbuf_use_const(&properties, ofpbuf_pull(msg, len), len);
 
-    error = ofputil_port_from_ofp11(op->port_no, &pp->port_no);
+    enum ofperr error = ofputil_port_from_ofp11(op->port_no, &pp->port_no);
     if (error) {
         return error;
     }
@@ -3845,6 +4012,8 @@ ofputil_pull_ofp14_port(struct ofputil_phy_port *pp, struct ofpbuf *msg)
     pp->config = ntohl(op->config) & OFPPC11_ALL;
     pp->state = ntohl(op->state) & OFPPS11_ALL;
 
+    struct ofpbuf properties = ofpbuf_const_initializer(ofpbuf_pull(msg, len),
+                                                        len);
     while (properties.size > 0) {
         struct ofpbuf payload;
         enum ofperr error;
@@ -3973,11 +4142,9 @@ enum ofperr
 ofputil_decode_port_desc_stats_request(const struct ofp_header *request,
                                        ofp_port_t *port)
 {
-    struct ofpbuf b;
-    enum ofpraw raw;
-
-    ofpbuf_use_const(&b, request, ntohs(request->length));
-    raw = ofpraw_pull_assert(&b);
+    struct ofpbuf b = ofpbuf_const_initializer(request,
+                                               ntohs(request->length));
+    enum ofpraw raw = ofpraw_pull_assert(&b);
     if (raw == OFPRAW_OFPST10_PORT_DESC_REQUEST) {
         *port = OFPP_ANY;
         return 0;
@@ -4041,13 +4208,10 @@ static bool
 ofputil_decode_switch_config(const struct ofp_header *oh,
                              struct ofputil_switch_config *config)
 {
-    const struct ofp_switch_config *osc;
-    struct ofpbuf b;
-
-    ofpbuf_use_const(&b, oh, ntohs(oh->length));
+    struct ofpbuf b = ofpbuf_const_initializer(oh, ntohs(oh->length));
     ofpraw_pull_assert(&b);
-    osc = ofpbuf_pull(&b, sizeof *osc);
 
+    const struct ofp_switch_config *osc = ofpbuf_pull(&b, sizeof *osc);
     config->frag = ntohs(osc->flags) & OFPC_FRAG_MASK;
     config->miss_send_len = ntohs(osc->miss_send_len);
 
@@ -4140,23 +4304,18 @@ ofputil_capabilities_mask(enum ofp_version ofp_version)
     }
 }
 
-/* Decodes an OpenFlow 1.0 or 1.1 "switch_features" structure 'osf' into an
- * abstract representation in '*features'.  Initializes '*b' to iterate over
- * the OpenFlow port structures following 'osf' with later calls to
- * ofputil_pull_phy_port().  Returns 0 if successful, otherwise an
- * OFPERR_* value.  */
+/* Pulls an OpenFlow "switch_features" structure from 'b' and decodes it into
+ * an abstract representation in '*features', readying 'b' to iterate over the
+ * OpenFlow port structures following 'osf' with later calls to
+ * ofputil_pull_phy_port().  Returns 0 if successful, otherwise an OFPERR_*
+ * value.  */
 enum ofperr
-ofputil_decode_switch_features(const struct ofp_header *oh,
-                               struct ofputil_switch_features *features,
-                               struct ofpbuf *b)
+ofputil_pull_switch_features(struct ofpbuf *b,
+                             struct ofputil_switch_features *features)
 {
-    const struct ofp_switch_features *osf;
-    enum ofpraw raw;
-
-    ofpbuf_use_const(b, oh, ntohs(oh->length));
-    raw = ofpraw_pull_assert(b);
-
-    osf = ofpbuf_pull(b, sizeof *osf);
+    const struct ofp_header *oh = b->data;
+    enum ofpraw raw = ofpraw_pull_assert(b);
+    const struct ofp_switch_features *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;
@@ -4321,14 +4480,10 @@ 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));
+    struct ofpbuf b = ofpbuf_const_initializer(oh, ntohs(oh->length));
     ofpraw_pull_assert(&b);
-    ops = ofpbuf_pull(&b, sizeof *ops);
 
+    const struct ofp_port_status *ops = ofpbuf_pull(&b, sizeof *ops);
     if (ops->reason != OFPPR_ADD &&
         ops->reason != OFPPR_DELETE &&
         ops->reason != OFPPR_MODIFY) {
@@ -4336,7 +4491,7 @@ ofputil_decode_port_status(const struct ofp_header *oh,
     }
     ps->reason = ops->reason;
 
-    retval = ofputil_pull_phy_port(oh->version, &b, &ps->desc);
+    int retval = ofputil_pull_phy_port(oh->version, &b, &ps->desc);
     ovs_assert(retval != EOF);
     return retval;
 }
@@ -4404,12 +4559,8 @@ enum ofperr
 ofputil_decode_port_mod(const struct ofp_header *oh,
                         struct ofputil_port_mod *pm, bool loose)
 {
-    enum ofpraw raw;
-    struct ofpbuf b;
-
-    ofpbuf_use_const(&b, oh, ntohs(oh->length));
-    raw = ofpraw_pull_assert(&b);
-
+    struct ofpbuf b = ofpbuf_const_initializer(oh, ntohs(oh->length));
+    enum ofpraw raw = ofpraw_pull_assert(&b);
     if (raw == OFPRAW_OFPT10_PORT_MOD) {
         const struct ofp10_port_mod *opm = b.data;
 
@@ -4680,33 +4831,26 @@ int
 ofputil_decode_table_features(struct ofpbuf *msg,
                               struct ofputil_table_features *tf, bool loose)
 {
-    const struct ofp_header *oh;
-    struct ofp13_table_features *otf;
-    struct ofpbuf properties;
-    unsigned int len;
-
     memset(tf, 0, sizeof *tf);
 
     if (!msg->header) {
         ofpraw_pull_assert(msg);
     }
-    oh = msg->header;
 
     if (!msg->size) {
         return EOF;
     }
 
+    const struct ofp_header *oh = msg->header;
+    struct ofp13_table_features *otf = msg->data;
     if (msg->size < sizeof *otf) {
         return OFPERR_OFPBPC_BAD_LEN;
     }
 
-    otf = msg->data;
-    len = ntohs(otf->length);
+    unsigned int len = ntohs(otf->length);
     if (len < sizeof *otf || len % 8 || len > msg->size) {
         return OFPERR_OFPBPC_BAD_LEN;
     }
-    ofpbuf_use_const(&properties, ofpbuf_pull(msg, len), len);
-    ofpbuf_pull(&properties, sizeof *otf);
 
     tf->table_id = otf->table_id;
     if (tf->table_id == OFPTT_ALL) {
@@ -4727,6 +4871,9 @@ ofputil_decode_table_features(struct ofpbuf *msg,
     }
     tf->max_entries = ntohl(otf->max_entries);
 
+    struct ofpbuf properties = ofpbuf_const_initializer(ofpbuf_pull(msg, len),
+                                                        len);
+    ofpbuf_pull(&properties, sizeof *otf);
     while (properties.size > 0) {
         struct ofpbuf payload;
         enum ofperr error;
@@ -4971,10 +5118,6 @@ ofputil_decode_table_desc(struct ofpbuf *msg,
                           struct ofputil_table_desc *td,
                           enum ofp_version version)
 {
-    struct ofp14_table_desc *otd;
-    struct ofpbuf properties;
-    size_t length;
-
     memset(td, 0, sizeof *td);
 
     if (!msg->header) {
@@ -4985,7 +5128,7 @@ ofputil_decode_table_desc(struct ofpbuf *msg,
         return EOF;
     }
 
-    otd = ofpbuf_try_pull(msg, sizeof *otd);
+    struct ofp14_table_desc *otd = ofpbuf_try_pull(msg, sizeof *otd);
     if (!otd) {
         VLOG_WARN_RL(&bad_ofmsg_rl, "OFP14_TABLE_DESC reply has %"PRIu32" "
                      "leftover bytes at end", msg->size);
@@ -4993,19 +5136,20 @@ ofputil_decode_table_desc(struct ofpbuf *msg,
     }
 
     td->table_id = otd->table_id;
-    length = ntohs(otd->length);
+    size_t length = ntohs(otd->length);
     if (length < sizeof *otd || length - sizeof *otd > msg->size) {
         VLOG_WARN_RL(&bad_ofmsg_rl, "OFP14_TABLE_DESC reply claims invalid "
                      "length %"PRIuSIZE, length);
         return OFPERR_OFPBRC_BAD_LEN;
     }
     length -= sizeof *otd;
-    ofpbuf_use_const(&properties, ofpbuf_pull(msg, length), length);
 
     td->eviction = ofputil_decode_table_eviction(otd->config, version);
     td->vacancy = ofputil_decode_table_vacancy(otd->config, version);
     td->eviction_flags = UINT32_MAX;
 
+    struct ofpbuf properties = ofpbuf_const_initializer(
+        ofpbuf_pull(msg, length), length);
     while (properties.size > 0) {
         struct ofpbuf payload;
         enum ofperr error;
@@ -5249,17 +5393,14 @@ enum ofperr
 ofputil_decode_table_mod(const struct ofp_header *oh,
                          struct ofputil_table_mod *pm)
 {
-    enum ofpraw raw;
-    struct ofpbuf b;
-
     memset(pm, 0, sizeof *pm);
     pm->miss = OFPUTIL_TABLE_MISS_DEFAULT;
     pm->eviction = OFPUTIL_TABLE_EVICTION_DEFAULT;
     pm->eviction_flags = UINT32_MAX;
     pm->vacancy = OFPUTIL_TABLE_VACANCY_DEFAULT;
-    ofpbuf_use_const(&b, oh, ntohs(oh->length));
-    raw = ofpraw_pull_assert(&b);
 
+    struct ofpbuf b = ofpbuf_const_initializer(oh, ntohs(oh->length));
+    enum ofpraw raw = ofpraw_pull_assert(&b);
     if (raw == OFPRAW_OFPT11_TABLE_MOD) {
         const struct ofp11_table_mod *otm = b.data;
 
@@ -5373,12 +5514,8 @@ enum ofperr
 ofputil_decode_role_message(const struct ofp_header *oh,
                             struct ofputil_role_request *rr)
 {
-    struct ofpbuf b;
-    enum ofpraw raw;
-
-    ofpbuf_use_const(&b, oh, ntohs(oh->length));
-    raw = ofpraw_pull_assert(&b);
-
+    struct ofpbuf b = ofpbuf_const_initializer(oh, ntohs(oh->length));
+    enum ofpraw raw = ofpraw_pull_assert(&b);
     if (raw == OFPRAW_OFPT12_ROLE_REQUEST ||
         raw == OFPRAW_OFPT12_ROLE_REPLY) {
         const struct ofp12_role_request *orr = b.msg;
@@ -5492,15 +5629,11 @@ enum ofperr
 ofputil_decode_role_status(const struct ofp_header *oh,
                            struct ofputil_role_status *rs)
 {
-    struct ofpbuf b;
-    enum ofpraw raw;
-    const struct ofp14_role_status *r;
-
-    ofpbuf_use_const(&b, oh, ntohs(oh->length));
-    raw = ofpraw_pull_assert(&b);
+    struct ofpbuf b = ofpbuf_const_initializer(oh, ntohs(oh->length));
+    enum ofpraw raw = ofpraw_pull_assert(&b);
     ovs_assert(raw == OFPRAW_OFPT14_ROLE_STATUS);
 
-    r = b.msg;
+    const struct ofp14_role_status *r = b.msg;
     if (r->role != htonl(OFPCR12_ROLE_NOCHANGE) &&
         r->role != htonl(OFPCR12_ROLE_EQUAL) &&
         r->role != htonl(OFPCR12_ROLE_MASTER) &&
@@ -5559,10 +5692,7 @@ enum ofperr
 ofputil_decode_requestforward(const struct ofp_header *outer,
                               struct ofputil_requestforward *rf)
 {
-    struct ofpbuf b;
-    enum ofperr error;
-
-    ofpbuf_use_const(&b, outer, ntohs(outer->length));
+    struct ofpbuf b = ofpbuf_const_initializer(outer, ntohs(outer->length));
 
     /* Skip past outer message. */
     enum ofpraw outer_raw = ofpraw_pull_assert(&b);
@@ -5583,7 +5713,7 @@ ofputil_decode_requestforward(const struct ofp_header *outer,
 
     /* Parse inner message. */
     enum ofptype type;
-    error = ofptype_decode(&type, inner);
+    enum ofperr error = ofptype_decode(&type, inner);
     if (error) {
         return error;
     }
@@ -6393,13 +6523,11 @@ make_echo_request(enum ofp_version ofp_version)
 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));
+    struct ofpbuf rq_buf = ofpbuf_const_initializer(rq, ntohs(rq->length));
     ofpraw_pull_assert(&rq_buf);
 
-    reply = ofpraw_alloc_reply(OFPRAW_OFPT_ECHO_REPLY, rq, rq_buf.size);
+    struct ofpbuf *reply = ofpraw_alloc_reply(OFPRAW_OFPT_ECHO_REPLY,
+                                              rq, rq_buf.size);
     ofpbuf_put(reply, rq_buf.data, rq_buf.size);
     return reply;
 }
@@ -7148,24 +7276,18 @@ static enum ofperr
 ofputil_pull_ofp14_port_stats(struct ofputil_port_stats *ops,
                               struct ofpbuf *msg)
 {
-    const struct ofp14_port_stats *ps14;
-    struct ofpbuf properties;
-    enum ofperr error;
-    size_t len;
-
-    ps14 = ofpbuf_try_pull(msg, sizeof *ps14);
+    const struct ofp14_port_stats *ps14 = ofpbuf_try_pull(msg, sizeof *ps14);
     if (!ps14) {
         return OFPERR_OFPBRC_BAD_LEN;
     }
 
-    len = ntohs(ps14->length);
+    size_t len = ntohs(ps14->length);
     if (len < sizeof *ps14 || len - sizeof *ps14 > msg->size) {
         return OFPERR_OFPBRC_BAD_LEN;
     }
     len -= sizeof *ps14;
-    ofpbuf_use_const(&properties, ofpbuf_pull(msg, len), len);
 
-    error = ofputil_port_from_ofp11(ps14->port_no, &ops->port_no);
+    enum ofperr error = ofputil_port_from_ofp11(ps14->port_no, &ops->port_no);
     if (error) {
         return error;
     }
@@ -7185,6 +7307,8 @@ ofputil_pull_ofp14_port_stats(struct ofputil_port_stats *ops,
     ops->stats.rx_crc_errors = UINT64_MAX;
     ops->stats.collisions = UINT64_MAX;
 
+    struct ofpbuf properties = ofpbuf_const_initializer(ofpbuf_pull(msg, len),
+                                                        len);
     while (properties.size > 0) {
         struct ofpbuf payload;
         enum ofperr error;
@@ -7218,16 +7342,15 @@ ofputil_pull_ofp14_port_stats(struct ofputil_port_stats *ops,
 size_t
 ofputil_count_port_stats(const struct ofp_header *oh)
 {
-    struct ofputil_port_stats ps;
-    struct ofpbuf b;
-    size_t n = 0;
-
-    ofpbuf_use_const(&b, oh, ntohs(oh->length));
+    struct ofpbuf b = ofpbuf_const_initializer(oh, ntohs(oh->length));
     ofpraw_pull_assert(&b);
-    while (!ofputil_decode_port_stats(&ps, &b)) {
-        n++;
+
+    for (size_t n = 0; ; n++) {
+        struct ofputil_port_stats ps;
+        if (ofputil_decode_port_stats(&ps, &b)) {
+            return n;
+        }
     }
-    return n;
 }
 
 /* Converts an OFPST_PORT_STATS reply in 'msg' into an abstract
@@ -7477,11 +7600,8 @@ ofputil_uninit_group_desc(struct ofputil_group_desc *gd)
 uint32_t
 ofputil_decode_group_desc_request(const struct ofp_header *oh)
 {
-    struct ofpbuf request;
-    enum ofpraw raw;
-
-    ofpbuf_use_const(&request, oh, ntohs(oh->length));
-    raw = ofpraw_pull_assert(&request);
+    struct ofpbuf request = ofpbuf_const_initializer(oh, ntohs(oh->length));
+    enum ofpraw raw = ofpraw_pull_assert(&request);
     if (raw == OFPRAW_OFPST11_GROUP_DESC_REQUEST) {
         return OFPG_ALL;
     } else if (raw == OFPRAW_OFPST15_GROUP_DESC_REQUEST) {
@@ -8011,7 +8131,6 @@ ofputil_pull_ofp15_buckets(struct ofpbuf *msg, size_t buckets_length,
         struct ofputil_bucket *bucket = NULL;
         struct ofpbuf ofpacts;
         enum ofperr err = OFPERR_OFPGMFC_BAD_BUCKET;
-        struct ofpbuf properties;
         size_t ob_len, actions_len, properties_len;
         ovs_be32 watch_port = ofputil_port_to_ofp11(OFPP_ANY);
         ovs_be32 watch_group = htonl(OFPG_ANY);
@@ -8054,9 +8173,8 @@ ofputil_pull_ofp15_buckets(struct ofpbuf *msg, size_t buckets_length,
         }
 
         properties_len = ob_len - sizeof *ob - actions_len;
-        ofpbuf_use_const(&properties, ofpbuf_pull(msg, properties_len),
-                         properties_len);
-
+        struct ofpbuf properties = ofpbuf_const_initializer(
+            ofpbuf_pull(msg, properties_len), properties_len);
         while (properties.size > 0) {
             struct ofpbuf payload;
             uint64_t type;
@@ -8228,11 +8346,8 @@ parse_ofp15_group_properties(struct ofpbuf *msg,
                              struct ofputil_group_props *gp,
                              size_t properties_len)
 {
-    struct ofpbuf properties;
-
-    ofpbuf_use_const(&properties, ofpbuf_pull(msg, properties_len),
-                     properties_len);
-
+    struct ofpbuf properties = ofpbuf_const_initializer(
+        ofpbuf_pull(msg, properties_len), properties_len);
     while (properties.size > 0) {
         struct ofpbuf payload;
         enum ofperr error;
@@ -8651,16 +8766,13 @@ enum ofperr
 ofputil_decode_group_mod(const struct ofp_header *oh,
                          struct ofputil_group_mod *gm)
 {
-    enum ofp_version ofp_version = oh->version;
-    struct ofpbuf msg;
-    struct ofputil_bucket *bucket;
-    enum ofperr err;
+    ofputil_init_group_properties(&gm->props);
 
-    ofpbuf_use_const(&msg, oh, ntohs(oh->length));
+    enum ofp_version ofp_version = oh->version;
+    struct ofpbuf msg = ofpbuf_const_initializer(oh, ntohs(oh->length));
     ofpraw_pull_assert(&msg);
 
-    ofputil_init_group_properties(&gm->props);
-
+    enum ofperr err;
     switch (ofp_version)
     {
     case OFP11_VERSION:
@@ -8678,7 +8790,6 @@ ofputil_decode_group_mod(const struct ofp_header *oh,
     default:
         OVS_NOT_REACHED();
     }
-
     if (err) {
         return err;
     }
@@ -8712,6 +8823,7 @@ ofputil_decode_group_mod(const struct ofp_header *oh,
         return OFPERR_OFPGMFC_BAD_COMMAND;
     }
 
+    struct ofputil_bucket *bucket;
     LIST_FOR_EACH (bucket, list_node, &gm->buckets) {
         if (bucket->weight && gm->type != OFPGT11_SELECT) {
             return OFPERR_OFPGMFC_INVALID_GROUP;
@@ -8818,16 +8930,15 @@ ofputil_encode_queue_stats_request(enum ofp_version ofp_version,
 size_t
 ofputil_count_queue_stats(const struct ofp_header *oh)
 {
-    struct ofputil_queue_stats qs;
-    struct ofpbuf b;
-    size_t n = 0;
-
-    ofpbuf_use_const(&b, oh, ntohs(oh->length));
+    struct ofpbuf b = ofpbuf_const_initializer(oh, ntohs(oh->length));
     ofpraw_pull_assert(&b);
-    while (!ofputil_decode_queue_stats(&qs, &b)) {
-        n++;
+
+    for (size_t n = 0; ; n++) {
+        struct ofputil_queue_stats qs;
+        if (ofputil_decode_queue_stats(&qs, &b)) {
+            return n;
+        }
     }
-    return n;
 }
 
 static enum ofperr
@@ -9048,15 +9159,11 @@ enum ofperr
 ofputil_decode_bundle_ctrl(const struct ofp_header *oh,
                            struct ofputil_bundle_ctrl_msg *msg)
 {
-    struct ofpbuf b;
-    enum ofpraw raw;
-    const struct ofp14_bundle_ctrl_msg *m;
-
-    ofpbuf_use_const(&b, oh, ntohs(oh->length));
-    raw = ofpraw_pull_assert(&b);
+    struct ofpbuf b = ofpbuf_const_initializer(oh, ntohs(oh->length));
+    enum ofpraw raw = ofpraw_pull_assert(&b);
     ovs_assert(raw == OFPRAW_OFPT14_BUNDLE_CONTROL);
 
-    m = b.msg;
+    const struct ofp14_bundle_ctrl_msg *m = b.msg;
     msg->bundle_id = ntohl(m->bundle_id);
     msg->type = ntohs(m->type);
     msg->flags = ntohs(m->flags);
@@ -9206,20 +9313,13 @@ ofputil_is_bundlable(enum ofptype type)
 enum ofperr
 ofputil_decode_bundle_add(const struct ofp_header *oh,
                           struct ofputil_bundle_add_msg *msg,
-                          enum ofptype *type_ptr)
+                          enum ofptype *typep)
 {
-    const struct ofp14_bundle_ctrl_msg *m;
-    struct ofpbuf b;
-    enum ofpraw raw;
-    size_t inner_len;
-    enum ofperr error;
-    enum ofptype type;
-
-    ofpbuf_use_const(&b, oh, ntohs(oh->length));
-    raw = ofpraw_pull_assert(&b);
+    struct ofpbuf b = ofpbuf_const_initializer(oh, ntohs(oh->length));
+    enum ofpraw raw = ofpraw_pull_assert(&b);
     ovs_assert(raw == OFPRAW_OFPT14_BUNDLE_ADD_MESSAGE);
 
-    m = ofpbuf_pull(&b, sizeof *m);
+    const struct ofp14_bundle_ctrl_msg *m = ofpbuf_pull(&b, sizeof *m);
     msg->bundle_id = ntohl(m->bundle_id);
     msg->flags = ntohs(m->flags);
 
@@ -9227,7 +9327,7 @@ ofputil_decode_bundle_add(const struct ofp_header *oh,
     if (msg->msg->version != oh->version) {
         return OFPERR_NXBFC_BAD_VERSION;
     }
-    inner_len = ntohs(msg->msg->length);
+    size_t inner_len = ntohs(msg->msg->length);
     if (inner_len < sizeof(struct ofp_header) || inner_len > b.size) {
         return OFPERR_OFPBFC_MSG_BAD_LEN;
     }
@@ -9236,21 +9336,22 @@ ofputil_decode_bundle_add(const struct ofp_header *oh,
     }
 
     /* Reject unbundlable messages. */
-    if (!type_ptr) {
-        type_ptr = &type;
-    }
-    error = ofptype_decode(type_ptr, msg->msg);
+    enum ofptype type;
+    enum ofperr error = ofptype_decode(&type, msg->msg);
     if (error) {
         VLOG_WARN_RL(&bad_ofmsg_rl, "OFPT14_BUNDLE_ADD_MESSAGE contained "
                      "message is unparsable (%s)", ofperr_get_name(error));
         return OFPERR_OFPBFC_MSG_UNSUP; /* 'error' would be confusing. */
     }
 
-    if (!ofputil_is_bundlable(*type_ptr)) {
+    if (!ofputil_is_bundlable(type)) {
         VLOG_WARN_RL(&bad_ofmsg_rl, "%s message not allowed inside "
-                     "OFPT14_BUNDLE_ADD_MESSAGE", ofptype_get_name(*type_ptr));
+                     "OFPT14_BUNDLE_ADD_MESSAGE", ofptype_get_name(type));
         return OFPERR_OFPBFC_MSG_UNSUP;
     }
+    if (typep) {
+        *typep = type;
+    }
 
     return 0;
 }
@@ -9348,13 +9449,10 @@ enum ofperr
 ofputil_decode_tlv_table_mod(const struct ofp_header *oh,
                                 struct ofputil_tlv_table_mod *ttm)
 {
-    struct ofpbuf msg;
-    struct nx_tlv_table_mod *nx_ttm;
-
-    ofpbuf_use_const(&msg, oh, ntohs(oh->length));
+    struct ofpbuf msg = ofpbuf_const_initializer(oh, ntohs(oh->length));
     ofpraw_pull_assert(&msg);
 
-    nx_ttm = ofpbuf_pull(&msg, sizeof *nx_ttm);
+    struct nx_tlv_table_mod *nx_ttm = ofpbuf_pull(&msg, sizeof *nx_ttm);
     ttm->command = ntohs(nx_ttm->command);
     if (ttm->command > NXTTMC_CLEAR) {
         VLOG_WARN_RL(&bad_ofmsg_rl,
@@ -9394,13 +9492,10 @@ enum ofperr
 ofputil_decode_tlv_table_reply(const struct ofp_header *oh,
                                   struct ofputil_tlv_table_reply *ttr)
 {
-    struct ofpbuf msg;
-    struct nx_tlv_table_reply *nx_ttr;
-
-    ofpbuf_use_const(&msg, oh, ntohs(oh->length));
+    struct ofpbuf msg = ofpbuf_const_initializer(oh, ntohs(oh->length));
     ofpraw_pull_assert(&msg);
 
-    nx_ttr = ofpbuf_pull(&msg, sizeof *nx_ttr);
+    struct nx_tlv_table_reply *nx_ttr = ofpbuf_pull(&msg, sizeof *nx_ttr);
     ttr->max_option_space = ntohl(nx_ttr->max_option_space);
     ttr->max_fields = ntohs(nx_ttr->max_fields);
 
@@ -9515,6 +9610,15 @@ decode_async_mask(ovs_be32 src,
         }
     }
 
+    if (ap->oam == OAM_PACKET_IN) {
+        if (mask & (1u << OFPR_NO_MATCH)) {
+            mask |= 1u << OFPR_EXPLICIT_MISS;
+            if (version < OFP13_VERSION) {
+                mask |= 1u << OFPR_IMPLICIT_MISS;
+            }
+        }
+    }
+
     uint32_t *array = ap->master ? dst->master : dst->slave;
     array[ap->oam] = mask;
     return 0;
@@ -9595,11 +9699,8 @@ ofputil_decode_set_async_config(const struct ofp_header *oh, bool loose,
                                 const struct ofputil_async_cfg *basis,
                                 struct ofputil_async_cfg *ac)
 {
-    enum ofpraw raw;
-    struct ofpbuf b;
-
-    ofpbuf_use_const(&b, oh, ntohs(oh->length));
-    raw = ofpraw_pull_assert(&b);
+    struct ofpbuf b = ofpbuf_const_initializer(oh, ntohs(oh->length));
+    enum ofpraw raw = ofpraw_pull_assert(&b);
 
     if (raw == OFPRAW_OFPT13_SET_ASYNC ||
         raw == OFPRAW_NXT_SET_ASYNC_CONFIG ||
@@ -9614,7 +9715,8 @@ ofputil_decode_set_async_config(const struct ofp_header *oh, bool loose,
         decode_legacy_async_masks(msg->flow_removed_mask, OAM_FLOW_REMOVED,
                                   oh->version, ac);
     } else if (raw == OFPRAW_OFPT14_SET_ASYNC ||
-               raw == OFPRAW_OFPT14_GET_ASYNC_REPLY) {
+               raw == OFPRAW_OFPT14_GET_ASYNC_REPLY ||
+               raw == OFPRAW_NXT_SET_ASYNC_CONFIG2) {
         *ac = *basis;
         while (b.size > 0) {
             struct ofpbuf property;
@@ -9695,8 +9797,8 @@ ofputil_put_async_config__(const struct ofputil_async_cfg *ac,
 /* Encodes and returns a reply to the OFPT_GET_ASYNC_REQUEST in 'oh' that
  * states that the asynchronous message configuration is 'ac'. */
 struct ofpbuf *
-ofputil_encode_get_async_config(const struct ofp_header *oh,
-                                const struct ofputil_async_cfg *ac)
+ofputil_encode_get_async_reply(const struct ofp_header *oh,
+                               const struct ofputil_async_cfg *ac)
 {
     struct ofpbuf *buf;
 
@@ -9712,13 +9814,47 @@ ofputil_encode_get_async_config(const struct ofp_header *oh,
     return buf;
 }
 
+/* Encodes and returns a message, in a format appropriate for OpenFlow version
+ * 'ofp_version', that sets the asynchronous message configuration to 'ac'.
+ *
+ * Specify 'oams' as a bitmap of OAM_* that indicate the asynchronous messages
+ * to configure.  OF1.0 through OF1.3 can't natively configure a subset of
+ * messages, so more messages than requested may be configured.  OF1.0 through
+ * OF1.3 also can't configure OVS extension OAM_* values, so if 'oam' includes
+ * any extensions then this function encodes an Open vSwitch extension message
+ * that does support configuring OVS extension OAM_*. */
+struct ofpbuf *
+ofputil_encode_set_async_config(const struct ofputil_async_cfg *ac,
+                                uint32_t oams, enum ofp_version ofp_version)
+{
+    enum ofpraw raw = (ofp_version >= OFP14_VERSION ? OFPRAW_OFPT14_SET_ASYNC
+                       : oams & OAM_EXTENSIONS ? OFPRAW_NXT_SET_ASYNC_CONFIG2
+                       : ofp_version >= OFP13_VERSION ? OFPRAW_OFPT13_SET_ASYNC
+                       : OFPRAW_NXT_SET_ASYNC_CONFIG);
+    struct ofpbuf *request = ofpraw_alloc(raw, ofp_version, 0);
+    ofputil_put_async_config__(ac, request,
+                               (raw == OFPRAW_OFPT14_SET_ASYNC ||
+                                raw == OFPRAW_NXT_SET_ASYNC_CONFIG2),
+                               ofp_version, oams);
+    return request;
+}
+
 struct ofputil_async_cfg
 ofputil_async_cfg_default(enum ofp_version version)
 {
+    /* We enable all of the OF1.4 reasons regardless of 'version' because the
+     * reasons added in OF1.4 just are just refinements of the OFPR_ACTION
+     * introduced in OF1.0, breaking it into more specific categories.  When we
+     * encode these for earlier OpenFlow versions, we translate them into
+     * OFPR_ACTION.  */
+    uint32_t pin = OFPR14_BITS & ~(1u << OFPR_INVALID_TTL);
+    pin |= 1u << OFPR_EXPLICIT_MISS;
+    if (version <= OFP12_VERSION) {
+        pin |= 1u << OFPR_IMPLICIT_MISS;
+    }
+
     return (struct ofputil_async_cfg) {
-        .master[OAM_PACKET_IN]
-            = ((version >= OFP14_VERSION ? OFPR14_BITS : OFPR10_BITS)
-               & ~(1u << OFPR_INVALID_TTL)),
+        .master[OAM_PACKET_IN] = pin,
 
         .master[OAM_FLOW_REMOVED]
             = (version >= OFP14_VERSION ? OFPRR14_BITS : OFPRR10_BITS),