flow: Enable matching on new field 'pkt_mark'.
[sliver-openvswitch.git] / lib / ofp-util.c
index de73eba..45ff0a1 100644 (file)
@@ -547,6 +547,71 @@ ofputil_match_to_ofp11_match(const struct match *match,
     ofmatch->wildcards = htonl(wc);
 }
 
+/* Returns the "typical" length of a match for 'protocol', for use in
+ * estimating space to preallocate. */
+int
+ofputil_match_typical_len(enum ofputil_protocol protocol)
+{
+    switch (protocol) {
+    case OFPUTIL_P_OF10_STD:
+    case OFPUTIL_P_OF10_STD_TID:
+        return sizeof(struct ofp10_match);
+
+    case OFPUTIL_P_OF10_NXM:
+    case OFPUTIL_P_OF10_NXM_TID:
+        return NXM_TYPICAL_LEN;
+
+    case OFPUTIL_P_OF11_STD:
+        return sizeof(struct ofp11_match);
+
+    case OFPUTIL_P_OF12_OXM:
+    case OFPUTIL_P_OF13_OXM:
+        return NXM_TYPICAL_LEN;
+
+    default:
+        NOT_REACHED();
+    }
+}
+
+/* Appends to 'b' an struct ofp11_match_header followed by a match that
+ * expresses 'match' properly for 'protocol', plus enough zero bytes to pad the
+ * data appended out to a multiple of 8.  'protocol' must be one that is usable
+ * in OpenFlow 1.1 or later.
+ *
+ * This function can cause 'b''s data to be reallocated.
+ *
+ * Returns the number of bytes appended to 'b', excluding the padding.  Never
+ * returns zero. */
+int
+ofputil_put_ofp11_match(struct ofpbuf *b, const struct match *match,
+                        enum ofputil_protocol 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:
+        NOT_REACHED();
+
+    case OFPUTIL_P_OF11_STD: {
+        struct ofp11_match *om;
+
+        /* Make sure that no padding is needed. */
+        BUILD_ASSERT_DECL(sizeof *om % 8 == 0);
+
+        om = ofpbuf_put_uninit(b, sizeof *om);
+        ofputil_match_to_ofp11_match(match, om);
+        return sizeof *om;
+    }
+
+    case OFPUTIL_P_OF12_OXM:
+    case OFPUTIL_P_OF13_OXM:
+        return oxm_put_match(b, match);
+    }
+
+    NOT_REACHED();
+}
+
 /* Given a 'dl_type' value in the format used in struct flow, returns the
  * corresponding 'dl_type' value for use in an ofp10_match or ofp11_match
  * structure. */
@@ -589,6 +654,7 @@ static const struct proto_abbrev proto_abbrevs[] = {
 enum ofputil_protocol ofputil_flow_dump_protocols[] = {
     OFPUTIL_P_OF13_OXM,
     OFPUTIL_P_OF12_OXM,
+    OFPUTIL_P_OF11_STD,
     OFPUTIL_P_OF10_NXM,
     OFPUTIL_P_OF10_STD,
 };
@@ -604,11 +670,12 @@ ofputil_protocols_from_ofp_version(enum ofp_version version)
     switch (version) {
     case OFP10_VERSION:
         return OFPUTIL_P_OF10_STD_ANY | OFPUTIL_P_OF10_NXM_ANY;
+    case OFP11_VERSION:
+        return OFPUTIL_P_OF11_STD;
     case OFP12_VERSION:
         return OFPUTIL_P_OF12_OXM;
     case OFP13_VERSION:
         return OFPUTIL_P_OF13_OXM;
-    case OFP11_VERSION:
     default:
         return 0;
     }
@@ -636,6 +703,8 @@ ofputil_protocol_to_ofp_version(enum ofputil_protocol protocol)
     case OFPUTIL_P_OF10_NXM:
     case OFPUTIL_P_OF10_NXM_TID:
         return OFP10_VERSION;
+    case OFPUTIL_P_OF11_STD:
+        return OFP11_VERSION;
     case OFPUTIL_P_OF12_OXM:
         return OFP12_VERSION;
     case OFPUTIL_P_OF13_OXM:
@@ -707,6 +776,9 @@ ofputil_protocol_set_tid(enum ofputil_protocol protocol, bool enable)
     case OFPUTIL_P_OF10_NXM_TID:
         return enable ? OFPUTIL_P_OF10_NXM_TID : OFPUTIL_P_OF10_NXM;
 
+    case OFPUTIL_P_OF11_STD:
+        return OFPUTIL_P_OF11_STD;
+
     case OFPUTIL_P_OF12_OXM:
         return OFPUTIL_P_OF12_OXM;
 
@@ -744,6 +816,9 @@ ofputil_protocol_set_base(enum ofputil_protocol cur,
     case OFPUTIL_P_OF10_NXM_TID:
         return ofputil_protocol_set_tid(OFPUTIL_P_OF10_NXM, tid);
 
+    case OFPUTIL_P_OF11_STD:
+        return ofputil_protocol_set_tid(OFPUTIL_P_OF11_STD, tid);
+
     case OFPUTIL_P_OF12_OXM:
         return ofputil_protocol_set_tid(OFPUTIL_P_OF12_OXM, tid);
 
@@ -778,6 +853,9 @@ ofputil_protocol_to_string(enum ofputil_protocol protocol)
     case OFPUTIL_P_OF10_STD_TID:
         return "OpenFlow10+table_id";
 
+    case OFPUTIL_P_OF11_STD:
+        return "OpenFlow11";
+
     case OFPUTIL_P_OF12_OXM:
         return "OXM-OpenFlow12";
 
@@ -1056,11 +1134,17 @@ ofputil_usable_protocols(const struct match *match)
         return OFPUTIL_P_NONE;
     }
 
-    /* skb_mark and skb_priority can't be sent in a flow_mod */
-    if (wc->masks.skb_mark || wc->masks.skb_priority) {
+    /* 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)) {
@@ -1086,8 +1170,19 @@ ofputil_usable_protocols(const struct match *match)
             | OFPUTIL_P_OF13_OXM;
     }
 
-    /* NXM and OXM support matching IPv6 traffic. */
-    if (match->flow.dl_type == htons(ETH_TYPE_IPV6)) {
+    /* 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;
     }
@@ -1112,12 +1207,6 @@ ofputil_usable_protocols(const struct match *match)
             | OFPUTIL_P_OF13_OXM;
     }
 
-    /* NXM and OXM support matching IPv6 flow label. */
-    if (wc->masks.ipv6_label) {
-        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
@@ -1210,7 +1299,7 @@ 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 = (const ovs_be32 *) (oheh + 1);
+    const ovs_be32 *bitmap = ALIGNED_CAST(const ovs_be32 *, oheh + 1);
     uint32_t allowed_versions;
 
     if (!bitmap_len || bitmap_len % sizeof *bitmap) {
@@ -1289,7 +1378,7 @@ ofputil_decode_hello(const struct ofp_header *oh, uint32_t *allowed_versions)
 
 /* Returns true if 'allowed_versions' needs to be accompanied by a version
  * bitmap to be correctly expressed in an OFPT_HELLO message. */
-static inline bool
+static bool
 should_send_version_bitmap(uint32_t allowed_versions)
 {
     return !is_pow2((allowed_versions >> 1) + 1);
@@ -1314,7 +1403,7 @@ ofputil_encode_hello(uint32_t 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);
-        *(ovs_be32 *)(oheh + 1) = htonl(allowed_versions);
+        *ALIGNED_CAST(ovs_be32 *, oheh + 1) = htonl(allowed_versions);
 
         ofpmsg_update_length(msg);
     }
@@ -1359,9 +1448,10 @@ ofputil_encode_set_protocol(enum ofputil_protocol current,
         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 are only one of each OpenFlow 1.2+ protocols and we already
+            /* 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();
 
@@ -1502,15 +1592,21 @@ ofputil_decode_flow_mod(struct ofputil_flow_mod *fm,
             return error;
         }
 
-        error = ofpacts_pull_openflow11_instructions(&b, b.size, ofm->table_id,
-                                                     ofpacts);
+        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) {
+        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;
@@ -1519,6 +1615,7 @@ ofputil_decode_flow_mod(struct ofputil_flow_mod *fm,
             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);
@@ -1616,6 +1713,7 @@ ofputil_decode_flow_mod(struct ofputil_flow_mod *fm,
                     : 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;
@@ -1631,6 +1729,371 @@ ofputil_decode_flow_mod(struct ofputil_flow_mod *fm,
     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)
@@ -1649,15 +2112,22 @@ ofputil_encode_flow_mod(const struct ofputil_flow_mod *fm,
     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),
-                           NXM_TYPICAL_LEN + fm->ofpacts_len);
+                           tailroom);
         ofm = ofpbuf_put_zeros(msg, sizeof *ofm);
-        if (fm->command == OFPFC_ADD) {
+        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;
@@ -1672,7 +2142,7 @@ ofputil_encode_flow_mod(const struct ofputil_flow_mod *fm,
         ofm->out_port = ofputil_port_to_ofp11(fm->out_port);
         ofm->out_group = htonl(OFPG11_ANY);
         ofm->flags = htons(fm->flags);
-        oxm_put_match(msg, &fm->match);
+        ofputil_put_ofp11_match(msg, &fm->match, protocol);
         ofpacts_put_openflow11_instructions(fm->ofpacts, fm->ofpacts_len, msg);
         break;
     }
@@ -1751,8 +2221,10 @@ ofputil_flow_mod_usable_protocols(const struct ofputil_flow_mod *fms,
 
         /* 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_OF12_OXM
-                | OFPUTIL_P_OF13_OXM;
+            usable_protocols &= (OFPUTIL_P_OF10_NXM_ANY
+                                 | OFPUTIL_P_OF11_STD
+                                 | OFPUTIL_P_OF12_OXM
+                                 | OFPUTIL_P_OF13_OXM);
         }
     }
 
@@ -1872,6 +2344,7 @@ ofputil_encode_flow_stats_request(const struct ofputil_flow_stats_request *fsr,
     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;
@@ -1880,14 +2353,14 @@ ofputil_encode_flow_stats_request(const struct ofputil_flow_stats_request *fsr,
                ? OFPRAW_OFPST11_AGGREGATE_REQUEST
                : OFPRAW_OFPST11_FLOW_REQUEST);
         msg = ofpraw_alloc(raw, ofputil_protocol_to_ofp_version(protocol),
-                          NXM_TYPICAL_LEN);
+                           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;
-        oxm_put_match(msg, &fsr->match);
+        ofputil_put_ofp11_match(msg, &fsr->match, protocol);
         break;
     }
 
@@ -2015,8 +2488,7 @@ ofputil_decode_flow_stats_reply(struct ofputil_flow_stats *fs,
         }
 
         if (ofpacts_pull_openflow11_instructions(msg, length - sizeof *ofs -
-                                                 padded_match_len,
-                                                 ofs->table_id, ofpacts)) {
+                                                 padded_match_len, ofpacts)) {
             VLOG_WARN_RL(&bad_ofmsg_rl, "OFPST_FLOW reply bad instructions");
             return EINVAL;
         }
@@ -2364,13 +2836,15 @@ ofputil_encode_flow_removed(const struct ofputil_flow_removed *fr,
     struct ofpbuf *msg;
 
     switch (protocol) {
+    case OFPUTIL_P_OF11_STD:
     case OFPUTIL_P_OF12_OXM:
     case OFPUTIL_P_OF13_OXM: {
         struct ofp12_flow_removed *ofr;
 
         msg = ofpraw_alloc_xid(OFPRAW_OFPT11_FLOW_REMOVED,
                                ofputil_protocol_to_ofp_version(protocol),
-                               htonl(0), NXM_TYPICAL_LEN);
+                               htonl(0),
+                               ofputil_match_typical_len(protocol));
         ofr = ofpbuf_put_zeros(msg, sizeof *ofr);
         ofr->cookie = fr->cookie;
         ofr->priority = htons(fr->priority);
@@ -2382,7 +2856,7 @@ ofputil_encode_flow_removed(const struct ofputil_flow_removed *fr,
         ofr->hard_timeout = htons(fr->hard_timeout);
         ofr->packet_count = htonll(fr->packet_count);
         ofr->byte_count = htonll(fr->byte_count);
-        oxm_put_match(msg, &fr->match);
+        ofputil_put_ofp11_match(msg, &fr->match, protocol);
         break;
     }
 
@@ -2449,6 +2923,7 @@ ofputil_decode_packet_in_finish(struct ofputil_packet_in *pin,
     pin->fmd.tun_dst = match->flow.tunnel.ip_dst;
     pin->fmd.metadata = match->flow.metadata;
     memcpy(pin->fmd.regs, match->flow.regs, sizeof pin->fmd.regs);
+    pin->fmd.pkt_mark = match->flow.pkt_mark;
 }
 
 enum ofperr
@@ -2563,6 +3038,10 @@ ofputil_packet_in_to_match(const struct ofputil_packet_in *pin,
         }
     }
 
+    if (pin->fmd.pkt_mark != 0) {
+        match_set_pkt_mark(match, pin->fmd.pkt_mark);
+    }
+
     match_set_in_port(match, pin->fmd.in_port);
 }
 
@@ -4716,6 +5195,21 @@ ofputil_port_stats_from_ofp13(struct ofputil_port_stats *ops,
     return error;
 }
 
+static size_t
+ofputil_get_port_stats_size(enum ofp_version ofp_version)
+{
+    switch (ofp_version) {
+    case OFP10_VERSION:
+        return sizeof(struct ofp10_port_stats);
+    case OFP11_VERSION:
+    case OFP12_VERSION:
+        return sizeof(struct ofp11_port_stats);
+    case OFP13_VERSION:
+        return sizeof(struct ofp13_port_stats);
+    default:
+        NOT_REACHED();
+    }
+}
 
 /* Returns the number of port stats elements in OFPTYPE_PORT_STATS_REPLY
  * message 'oh'. */
@@ -4727,9 +5221,7 @@ ofputil_count_port_stats(const struct ofp_header *oh)
     ofpbuf_use_const(&b, oh, ntohs(oh->length));
     ofpraw_pull_assert(&b);
 
-    BUILD_ASSERT(sizeof(struct ofp10_port_stats) ==
-                 sizeof(struct ofp11_port_stats));
-    return b.size / sizeof(struct ofp10_port_stats);
+    return b.size / ofputil_get_port_stats_size(oh->version);
 }
 
 /* Converts an OFPST_PORT_STATS reply in 'msg' into an abstract
@@ -4885,6 +5377,22 @@ ofputil_encode_queue_stats_request(enum ofp_version ofp_version,
     return request;
 }
 
+static size_t
+ofputil_get_queue_stats_size(enum ofp_version ofp_version)
+{
+    switch (ofp_version) {
+    case OFP10_VERSION:
+        return sizeof(struct ofp10_queue_stats);
+    case OFP11_VERSION:
+    case OFP12_VERSION:
+        return sizeof(struct ofp11_queue_stats);
+    case OFP13_VERSION:
+        return sizeof(struct ofp13_queue_stats);
+    default:
+        NOT_REACHED();
+    }
+}
+
 /* Returns the number of queue stats elements in OFPTYPE_QUEUE_STATS_REPLY
  * message 'oh'. */
 size_t
@@ -4895,9 +5403,7 @@ ofputil_count_queue_stats(const struct ofp_header *oh)
     ofpbuf_use_const(&b, oh, ntohs(oh->length));
     ofpraw_pull_assert(&b);
 
-    BUILD_ASSERT(sizeof(struct ofp10_queue_stats) ==
-                 sizeof(struct ofp11_queue_stats));
-    return b.size / sizeof(struct ofp10_queue_stats);
+    return b.size / ofputil_get_queue_stats_size(oh->version);
 }
 
 static enum ofperr
@@ -4906,9 +5412,10 @@ ofputil_queue_stats_from_ofp10(struct ofputil_queue_stats *oqs,
 {
     oqs->port_no = u16_to_ofp(ntohs(qs10->port_no));
     oqs->queue_id = ntohl(qs10->queue_id);
-    oqs->stats.tx_bytes = ntohll(get_32aligned_be64(&qs10->tx_bytes));
-    oqs->stats.tx_packets = ntohll(get_32aligned_be64(&qs10->tx_packets));
-    oqs->stats.tx_errors = ntohll(get_32aligned_be64(&qs10->tx_errors));
+    oqs->tx_bytes = ntohll(get_32aligned_be64(&qs10->tx_bytes));
+    oqs->tx_packets = ntohll(get_32aligned_be64(&qs10->tx_packets));
+    oqs->tx_errors = ntohll(get_32aligned_be64(&qs10->tx_errors));
+    oqs->duration_sec = oqs->duration_nsec = UINT32_MAX;
 
     return 0;
 }
@@ -4925,9 +5432,10 @@ ofputil_queue_stats_from_ofp11(struct ofputil_queue_stats *oqs,
     }
 
     oqs->queue_id = ntohl(qs11->queue_id);
-    oqs->stats.tx_bytes = ntohll(qs11->tx_bytes);
-    oqs->stats.tx_packets = ntohll(qs11->tx_packets);
-    oqs->stats.tx_errors = ntohll(qs11->tx_errors);
+    oqs->tx_bytes = ntohll(qs11->tx_bytes);
+    oqs->tx_packets = ntohll(qs11->tx_packets);
+    oqs->tx_errors = ntohll(qs11->tx_errors);
+    oqs->duration_sec = oqs->duration_nsec = UINT32_MAX;
 
     return 0;
 }
@@ -4936,11 +5444,10 @@ static enum ofperr
 ofputil_queue_stats_from_ofp13(struct ofputil_queue_stats *oqs,
                                const struct ofp13_queue_stats *qs13)
 {
-    enum ofperr error
-        = ofputil_queue_stats_from_ofp11(oqs, &qs13->qs);
+    enum ofperr error = ofputil_queue_stats_from_ofp11(oqs, &qs13->qs);
     if (!error) {
-        /* FIXME: Get qs13->duration_sec and qs13->duration_nsec,
-         * Add to netdev_queue_stats? */
+        oqs->duration_sec = ntohl(qs13->duration_sec);
+        oqs->duration_nsec = ntohl(qs13->duration_nsec);
     }
 
     return error;
@@ -5012,9 +5519,9 @@ ofputil_queue_stats_to_ofp10(const struct ofputil_queue_stats *oqs,
     qs10->port_no = htons(ofp_to_u16(oqs->port_no));
     memset(qs10->pad, 0, sizeof qs10->pad);
     qs10->queue_id = htonl(oqs->queue_id);
-    put_32aligned_be64(&qs10->tx_bytes, htonll(oqs->stats.tx_bytes));
-    put_32aligned_be64(&qs10->tx_packets, htonll(oqs->stats.tx_packets));
-    put_32aligned_be64(&qs10->tx_errors, htonll(oqs->stats.tx_errors));
+    put_32aligned_be64(&qs10->tx_bytes, htonll(oqs->tx_bytes));
+    put_32aligned_be64(&qs10->tx_packets, htonll(oqs->tx_packets));
+    put_32aligned_be64(&qs10->tx_errors, htonll(oqs->tx_errors));
 }
 
 static void
@@ -5023,9 +5530,9 @@ ofputil_queue_stats_to_ofp11(const struct ofputil_queue_stats *oqs,
 {
     qs11->port_no = ofputil_port_to_ofp11(oqs->port_no);
     qs11->queue_id = htonl(oqs->queue_id);
-    qs11->tx_bytes = htonll(oqs->stats.tx_bytes);
-    qs11->tx_packets = htonll(oqs->stats.tx_packets);
-    qs11->tx_errors = htonll(oqs->stats.tx_errors);
+    qs11->tx_bytes = htonll(oqs->tx_bytes);
+    qs11->tx_packets = htonll(oqs->tx_packets);
+    qs11->tx_errors = htonll(oqs->tx_errors);
 }
 
 static void
@@ -5033,10 +5540,13 @@ ofputil_queue_stats_to_ofp13(const struct ofputil_queue_stats *oqs,
                              struct ofp13_queue_stats *qs13)
 {
     ofputil_queue_stats_to_ofp11(oqs, &qs13->qs);
-    /* OF 1.3 adds duration fields */
-    /* FIXME: Need to implement queue alive duration (sec + nsec) */
-    qs13->duration_sec = htonl(~0);
-    qs13->duration_nsec = htonl(~0);
+    if (oqs->duration_sec != UINT32_MAX) {
+        qs13->duration_sec = htonl(oqs->duration_sec);
+        qs13->duration_nsec = htonl(oqs->duration_nsec);
+    } else {
+        qs13->duration_sec = htonl(UINT32_MAX);
+        qs13->duration_nsec = htonl(UINT32_MAX);
+    }
 }
 
 /* Encode a queue stat for 'oqs' and append it to 'replies'. */