Implement OpenFlow 1.4+ OFPTC_EVICTION.
[cascardo/ovs.git] / lib / ofp-util.c
index 34e6df4..2e9ae47 100644 (file)
@@ -41,6 +41,7 @@
 #include "openflow/netronome-ext.h"
 #include "packets.h"
 #include "random.h"
+#include "tun-metadata.h"
 #include "unaligned.h"
 #include "type-props.h"
 #include "openvswitch/vlog.h"
@@ -52,8 +53,11 @@ VLOG_DEFINE_THIS_MODULE(ofp_util);
  * in the peer and so there's not much point in showing a lot of them. */
 static struct vlog_rate_limit bad_ofmsg_rl = VLOG_RATE_LIMIT_INIT(1, 5);
 
-static enum ofputil_table_miss ofputil_table_miss_from_config(
-    ovs_be32 config_, enum ofp_version);
+static enum ofputil_table_eviction ofputil_decode_table_eviction(
+    ovs_be32 config, enum ofp_version);
+static ovs_be32 ofputil_encode_table_config(enum ofputil_table_miss,
+                                            enum ofputil_table_eviction,
+                                            enum ofp_version);
 
 struct ofp_prop_header {
     ovs_be16 type;
@@ -195,7 +199,7 @@ ofputil_netmask_to_wcbits(ovs_be32 netmask)
 void
 ofputil_wildcard_from_ofpfw10(uint32_t ofpfw, struct flow_wildcards *wc)
 {
-    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 31);
+    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 32);
 
     /* Initialize most of wc. */
     flow_wildcards_init_catchall(wc);
@@ -1317,7 +1321,7 @@ ofputil_decode_hello_bitmap(const struct ofp_hello_elem_header *oheh,
 
     /* Only use the first 32-bit element of the bitmap as that is all the
      * current implementation supports.  Subsequent elements are ignored which
-     * should have no effect on session negotiation until Open vSwtich supports
+     * should have no effect on session negotiation until Open vSwitch supports
      * wire-protocol versions greater than 31.
      */
     allowed_versions = ntohl(bitmap[0]);
@@ -1795,11 +1799,19 @@ ofputil_decode_flow_mod(struct ofputil_flow_mod *fm,
             fm->command = command & 0xff;
             fm->table_id = command >> 8;
         } else {
+            if (command > 0xff) {
+                VLOG_WARN_RL(&bad_ofmsg_rl, "flow_mod has explicit table_id "
+                             "but flow_mod_table_id extension is not enabled");
+            }
             fm->command = command;
             fm->table_id = 0xff;
         }
     }
 
+    if (fm->command > OFPFC_DELETE_STRICT) {
+        return OFPERR_OFPFMFC_BAD_COMMAND;
+    }
+
     error = ofpacts_pull_openflow_instructions(&b, b.size,
                                                oh->version, ofpacts);
     if (error) {
@@ -3295,24 +3307,6 @@ ofputil_encode_flow_removed(const struct ofputil_flow_removed *fr,
     return msg;
 }
 
-static void
-ofputil_decode_packet_in_finish(struct ofputil_packet_in *pin,
-                                struct match *match, struct ofpbuf *b)
-{
-    pin->packet = b->data;
-    pin->packet_len = b->size;
-
-    pin->fmd.in_port = match->flow.in_port.ofp_port;
-    pin->fmd.tun_id = match->flow.tunnel.tun_id;
-    pin->fmd.tun_src = match->flow.tunnel.ip_src;
-    pin->fmd.tun_dst = match->flow.tunnel.ip_dst;
-    pin->fmd.gbp_id = match->flow.tunnel.gbp_id;
-    pin->fmd.gbp_flags = match->flow.tunnel.gbp_flags;
-    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
 ofputil_decode_packet_in(struct ofputil_packet_in *pin,
                          const struct ofp_header *oh)
@@ -3327,7 +3321,6 @@ ofputil_decode_packet_in(struct ofputil_packet_in *pin,
     raw = ofpraw_pull_assert(&b);
     if (raw == OFPRAW_OFPT13_PACKET_IN || raw == OFPRAW_OFPT12_PACKET_IN) {
         const struct ofp13_packet_in *opi;
-        struct match match;
         int error;
         size_t packet_in_size;
 
@@ -3338,7 +3331,7 @@ ofputil_decode_packet_in(struct ofputil_packet_in *pin,
         }
 
         opi = ofpbuf_pull(&b, packet_in_size);
-        error = oxm_pull_match_loose(&b, &match);
+        error = oxm_pull_match_loose(&b, &pin->flow_metadata);
         if (error) {
             return error;
         }
@@ -3356,7 +3349,8 @@ ofputil_decode_packet_in(struct ofputil_packet_in *pin,
             pin->cookie = opi->cookie;
         }
 
-        ofputil_decode_packet_in_finish(pin, &match, &b);
+        pin->packet = b.data;
+        pin->packet_len = b.size;
     } else if (raw == OFPRAW_OFPT10_PACKET_IN) {
         const struct ofp10_packet_in *opi;
 
@@ -3365,12 +3359,14 @@ ofputil_decode_packet_in(struct ofputil_packet_in *pin,
         pin->packet = opi->data;
         pin->packet_len = b.size;
 
-        pin->fmd.in_port = u16_to_ofp(ntohs(opi->in_port));
+        match_init_catchall(&pin->flow_metadata);
+        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);
     } else if (raw == OFPRAW_OFPT11_PACKET_IN) {
         const struct ofp11_packet_in *opi;
+        ofp_port_t in_port;
         enum ofperr error;
 
         opi = ofpbuf_pull(&b, sizeof *opi);
@@ -3379,21 +3375,22 @@ ofputil_decode_packet_in(struct ofputil_packet_in *pin,
         pin->packet_len = b.size;
 
         pin->buffer_id = ntohl(opi->buffer_id);
-        error = ofputil_port_from_ofp11(opi->in_port, &pin->fmd.in_port);
+        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);
         pin->reason = opi->reason;
         pin->table_id = opi->table_id;
     } else if (raw == OFPRAW_NXT_PACKET_IN) {
         const struct nx_packet_in *npi;
-        struct match match;
         int error;
 
         npi = ofpbuf_pull(&b, sizeof *npi);
-        error = nx_pull_match_loose(&b, ntohs(npi->match_len), &match, NULL,
-                                    NULL);
+        error = nx_pull_match_loose(&b, ntohs(npi->match_len),
+                                    &pin->flow_metadata, NULL, NULL);
         if (error) {
             return error;
         }
@@ -3409,7 +3406,8 @@ ofputil_decode_packet_in(struct ofputil_packet_in *pin,
         pin->buffer_id = ntohl(npi->buffer_id);
         pin->total_len = ntohs(npi->total_len);
 
-        ofputil_decode_packet_in_finish(pin, &match, &b);
+        pin->packet = b.data;
+        pin->packet_len = b.size;
     } else {
         OVS_NOT_REACHED();
     }
@@ -3417,45 +3415,6 @@ ofputil_decode_packet_in(struct ofputil_packet_in *pin,
     return 0;
 }
 
-static void
-ofputil_packet_in_to_match(const struct ofputil_packet_in *pin,
-                           struct match *match)
-{
-    int i;
-
-    match_init_catchall(match);
-    if (pin->fmd.tun_id != htonll(0)) {
-        match_set_tun_id(match, pin->fmd.tun_id);
-    }
-    if (pin->fmd.tun_src != htonl(0)) {
-        match_set_tun_src(match, pin->fmd.tun_src);
-    }
-    if (pin->fmd.tun_dst != htonl(0)) {
-        match_set_tun_dst(match, pin->fmd.tun_dst);
-    }
-    if (pin->fmd.gbp_id != htons(0)) {
-        match_set_tun_gbp_id(match, pin->fmd.gbp_id);
-    }
-    if (pin->fmd.gbp_flags) {
-        match_set_tun_gbp_flags(match, pin->fmd.gbp_flags);
-    }
-    if (pin->fmd.metadata != htonll(0)) {
-        match_set_metadata(match, pin->fmd.metadata);
-    }
-
-    for (i = 0; i < FLOW_N_REGS; i++) {
-        if (pin->fmd.regs[i]) {
-            match_set_reg(match, i, pin->fmd.regs[i]);
-        }
-    }
-
-    if (pin->fmd.pkt_mark != 0) {
-        match_set_pkt_mark(match, pin->fmd.pkt_mark);
-    }
-
-    match_set_in_port(match, pin->fmd.in_port);
-}
-
 static struct ofpbuf *
 ofputil_encode_ofp10_packet_in(const struct ofputil_packet_in *pin)
 {
@@ -3466,7 +3425,7 @@ ofputil_encode_ofp10_packet_in(const struct ofputil_packet_in *pin)
                               htonl(0), pin->packet_len);
     opi = ofpbuf_put_zeros(packet, offsetof(struct ofp10_packet_in, data));
     opi->total_len = htons(pin->total_len);
-    opi->in_port = htons(ofp_to_u16(pin->fmd.in_port));
+    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);
 
@@ -3480,17 +3439,13 @@ ofputil_encode_nx_packet_in(const struct ofputil_packet_in *pin)
 {
     struct nx_packet_in *npi;
     struct ofpbuf *packet;
-    struct match match;
     size_t match_len;
 
-    ofputil_packet_in_to_match(pin, &match);
-
     /* The final argument is just an estimate of the space required. */
     packet = ofpraw_alloc_xid(OFPRAW_NXT_PACKET_IN, OFP10_VERSION,
-                              htonl(0), (sizeof(struct flow_metadata) * 2
-                                         + 2 + pin->packet_len));
+                              htonl(0), NXM_TYPICAL_LEN + 2 + pin->packet_len);
     ofpbuf_put_zeros(packet, sizeof *npi);
-    match_len = nx_put_match(packet, &match, 0, 0);
+    match_len = nx_put_match(packet, &pin->flow_metadata, 0, 0);
     ofpbuf_put_zeros(packet, 2);
     ofpbuf_put(packet, pin->packet, pin->packet_len);
 
@@ -3515,7 +3470,7 @@ ofputil_encode_ofp11_packet_in(const struct ofputil_packet_in *pin)
                               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->fmd.in_port);
+    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;
@@ -3531,7 +3486,6 @@ ofputil_encode_ofp12_packet_in(const struct ofputil_packet_in *pin,
                                enum ofputil_protocol protocol)
 {
     struct ofp13_packet_in *opi;
-    struct match match;
     enum ofpraw packet_in_raw;
     enum ofp_version packet_in_version;
     size_t packet_in_size;
@@ -3547,14 +3501,12 @@ ofputil_encode_ofp12_packet_in(const struct ofputil_packet_in *pin,
         packet_in_size = sizeof (struct ofp13_packet_in);
     }
 
-    ofputil_packet_in_to_match(pin, &match);
-
     /* The final argument is just an estimate of the space required. */
     packet = ofpraw_alloc_xid(packet_in_raw, packet_in_version,
-                              htonl(0), (sizeof(struct flow_metadata) * 2
-                                         + 2 + pin->packet_len));
+                              htonl(0), NXM_TYPICAL_LEN + 2 + pin->packet_len);
     ofpbuf_put_zeros(packet, packet_in_size);
-    oxm_put_match(packet, &match, ofputil_protocol_to_ofp_version(protocol));
+    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);
 
@@ -4695,7 +4647,15 @@ ofputil_decode_table_features(struct ofpbuf *msg,
     ovs_strlcpy(tf->name, otf->name, OFP_MAX_TABLE_NAME_LEN);
     tf->metadata_match = otf->metadata_match;
     tf->metadata_write = otf->metadata_write;
-    tf->miss_config = ofputil_table_miss_from_config(otf->config, oh->version);
+    tf->miss_config = OFPUTIL_TABLE_MISS_DEFAULT;
+    if (oh->version >= OFP14_VERSION) {
+        uint32_t caps = ntohl(otf->capabilities);
+        tf->supports_eviction = (caps & OFPTC14_EVICTION) != 0;
+        tf->supports_vacancy_events = (caps & OFPTC14_VACANCY_EVENTS) != 0;
+    } else {
+        tf->supports_eviction = -1;
+        tf->supports_vacancy_events = -1;
+    }
     tf->max_entries = ntohl(otf->max_entries);
 
     while (properties.size > 0) {
@@ -4903,7 +4863,14 @@ ofputil_append_table_features_reply(const struct ofputil_table_features *tf,
     ovs_strlcpy(otf->name, tf->name, sizeof otf->name);
     otf->metadata_match = tf->metadata_match;
     otf->metadata_write = tf->metadata_write;
-    otf->config = ofputil_table_miss_to_config(tf->miss_config, version);
+    if (version >= OFP14_VERSION) {
+        if (tf->supports_eviction) {
+            otf->capabilities |= htonl(OFPTC14_EVICTION);
+        }
+        if (tf->supports_vacancy_events) {
+            otf->capabilities |= htonl(OFPTC14_VACANCY_EVENTS);
+        }
+    }
     otf->max_entries = htonl(tf->max_entries);
 
     put_table_instruction_features(reply, &tf->nonmiss, 0, version);
@@ -4919,17 +4886,97 @@ ofputil_append_table_features_reply(const struct ofputil_table_features *tf,
     ofpmp_postappend(replies, start_ofs);
 }
 
-/* ofputil_table_mod */
+static enum ofperr
+parse_table_mod_eviction_property(struct ofpbuf *property,
+                                  struct ofputil_table_mod *tm)
+{
+    struct ofp14_table_mod_prop_eviction *ote = property->data;
+
+    if (property->size != sizeof *ote) {
+        return OFPERR_OFPBPC_BAD_LEN;
+    }
+
+    tm->eviction_flags = ntohl(ote->flags);
+    return 0;
+}
 
 /* Given 'config', taken from an OpenFlow 'version' message that specifies
  * table configuration (a table mod, table stats, or table features message),
- * returns the table miss configuration that it specifies.  */
+ * returns the table eviction configuration that it specifies.
+ *
+ * Only OpenFlow 1.4 and later specify table eviction configuration this way,
+ * so for other 'version' values this function always returns
+ * OFPUTIL_TABLE_EVICTION_DEFAULT. */
+static enum ofputil_table_eviction
+ofputil_decode_table_eviction(ovs_be32 config, enum ofp_version version)
+{
+    return (version < OFP14_VERSION ? OFPUTIL_TABLE_EVICTION_DEFAULT
+            : config & htonl(OFPTC14_EVICTION) ? OFPUTIL_TABLE_EVICTION_ON
+            : OFPUTIL_TABLE_EVICTION_OFF);
+}
+
+/* Returns a bitmap of OFPTC* values suitable for 'config' fields in various
+ * OpenFlow messages of the given 'version', based on the provided 'miss' and
+ * 'eviction' values. */
+static ovs_be32
+ofputil_encode_table_config(enum ofputil_table_miss miss,
+                            enum ofputil_table_eviction eviction,
+                            enum ofp_version version)
+{
+    /* See the section "OFPTC_* Table Configuration" in DESIGN.md for more
+     * information on the crazy evolution of this field. */
+    switch (version) {
+    case OFP10_VERSION:
+        /* OpenFlow 1.0 didn't have such a field, any value ought to do. */
+        return htonl(0);
+
+    case OFP11_VERSION:
+    case OFP12_VERSION:
+        /* OpenFlow 1.1 and 1.2 define only OFPTC11_TABLE_MISS_*. */
+        switch (miss) {
+        case OFPUTIL_TABLE_MISS_DEFAULT:
+            /* Really this shouldn't be used for encoding (the caller should
+             * provide a specific value) but I can't imagine that defaulting to
+             * the fall-through case here will hurt. */
+        case OFPUTIL_TABLE_MISS_CONTROLLER:
+        default:
+            return htonl(OFPTC11_TABLE_MISS_CONTROLLER);
+        case OFPUTIL_TABLE_MISS_CONTINUE:
+            return htonl(OFPTC11_TABLE_MISS_CONTINUE);
+        case OFPUTIL_TABLE_MISS_DROP:
+            return htonl(OFPTC11_TABLE_MISS_DROP);
+        }
+        OVS_NOT_REACHED();
+
+    case OFP13_VERSION:
+        /* OpenFlow 1.3 removed OFPTC11_TABLE_MISS_* and didn't define any new
+         * flags, so this is correct. */
+        return htonl(0);
+
+    case OFP14_VERSION:
+    case OFP15_VERSION:
+        /* OpenFlow 1.4 introduced OFPTC14_EVICTION and OFPTC14_VACANCY_EVENTS
+         * and we don't support the latter yet. */
+        return htonl(eviction == OFPUTIL_TABLE_EVICTION_ON
+                     ? OFPTC14_EVICTION : 0);
+    }
+
+    OVS_NOT_REACHED();
+}
+
+/* Given 'config', taken from an OpenFlow 'version' message that specifies
+ * table configuration (a table mod, table stats, or table features message),
+ * returns the table miss configuration that it specifies.
+ *
+ * Only OpenFlow 1.1 and 1.2 specify table miss configurations this way, so for
+ * other 'version' values this function always returns
+ * OFPUTIL_TABLE_MISS_DEFAULT. */
 static enum ofputil_table_miss
-ofputil_table_miss_from_config(ovs_be32 config_, enum ofp_version version)
+ofputil_decode_table_miss(ovs_be32 config_, enum ofp_version version)
 {
     uint32_t config = ntohl(config_);
 
-    if (version < OFP13_VERSION) {
+    if (version == OFP11_VERSION || version == OFP12_VERSION) {
         switch (config & OFPTC11_TABLE_MISS_MASK) {
         case OFPTC11_TABLE_MISS_CONTROLLER:
             return OFPUTIL_TABLE_MISS_CONTROLLER;
@@ -4949,32 +4996,6 @@ ofputil_table_miss_from_config(ovs_be32 config_, enum ofp_version version)
     }
 }
 
-/* Given a table miss configuration, returns the corresponding OpenFlow table
- * configuration for use in an OpenFlow message of the given 'version'. */
-ovs_be32
-ofputil_table_miss_to_config(enum ofputil_table_miss miss,
-                             enum ofp_version version)
-{
-    if (version < OFP13_VERSION) {
-        switch (miss) {
-        case OFPUTIL_TABLE_MISS_CONTROLLER:
-        case OFPUTIL_TABLE_MISS_DEFAULT:
-            return htonl(OFPTC11_TABLE_MISS_CONTROLLER);
-
-        case OFPUTIL_TABLE_MISS_CONTINUE:
-            return htonl(OFPTC11_TABLE_MISS_CONTINUE);
-
-        case OFPUTIL_TABLE_MISS_DROP:
-            return htonl(OFPTC11_TABLE_MISS_DROP);
-
-        default:
-            OVS_NOT_REACHED();
-        }
-    } else {
-        return htonl(0);
-    }
-}
-
 /* Decodes the OpenFlow "table mod" message in '*oh' into an abstract form in
  * '*pm'.  Returns 0 if successful, otherwise an OFPERR_* value. */
 enum ofperr
@@ -4984,6 +5005,10 @@ ofputil_decode_table_mod(const struct ofp_header *oh,
     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;
     ofpbuf_use_const(&b, oh, ntohs(oh->length));
     raw = ofpraw_pull_assert(&b);
 
@@ -4991,16 +5016,37 @@ ofputil_decode_table_mod(const struct ofp_header *oh,
         const struct ofp11_table_mod *otm = b.data;
 
         pm->table_id = otm->table_id;
-        pm->miss_config = ofputil_table_miss_from_config(otm->config,
-                                                         oh->version);
+        pm->miss = ofputil_decode_table_miss(otm->config, oh->version);
     } else if (raw == OFPRAW_OFPT14_TABLE_MOD) {
         const struct ofp14_table_mod *otm = ofpbuf_pull(&b, sizeof *otm);
 
         pm->table_id = otm->table_id;
-        pm->miss_config = ofputil_table_miss_from_config(otm->config,
-                                                         oh->version);
-        /* We do not understand any properties yet, so we do not bother
-         * parsing them. */
+        pm->miss = ofputil_decode_table_miss(otm->config, oh->version);
+        pm->eviction = ofputil_decode_table_eviction(otm->config, oh->version);
+        while (b.size > 0) {
+            struct ofpbuf property;
+            enum ofperr error;
+            uint16_t type;
+
+            error = ofputil_pull_property(&b, &property, &type);
+            if (error) {
+                return error;
+            }
+
+            switch (type) {
+            case OFPTMPT14_EVICTION:
+                error = parse_table_mod_eviction_property(&property, pm);
+                break;
+
+            default:
+                error = OFPERR_OFPBRC_BAD_TYPE;
+                break;
+            }
+
+            if (error) {
+                return error;
+            }
+        }
     } else {
         return OFPERR_OFPBRC_BAD_TYPE;
     }
@@ -5008,11 +5054,11 @@ ofputil_decode_table_mod(const struct ofp_header *oh,
     return 0;
 }
 
-/* Converts the abstract form of a "table mod" message in '*pm' into an OpenFlow
- * message suitable for 'protocol', and returns that encoded form in a buffer
- * owned by the caller. */
+/* Converts the abstract form of a "table mod" message in '*tm' into an
+ * OpenFlow message suitable for 'protocol', and returns that encoded form in a
+ * buffer owned by the caller. */
 struct ofpbuf *
-ofputil_encode_table_mod(const struct ofputil_table_mod *pm,
+ofputil_encode_table_mod(const struct ofputil_table_mod *tm,
                         enum ofputil_protocol protocol)
 {
     enum ofp_version ofp_version = ofputil_protocol_to_ofp_version(protocol);
@@ -5031,20 +5077,28 @@ ofputil_encode_table_mod(const struct ofputil_table_mod *pm,
 
         b = ofpraw_alloc(OFPRAW_OFPT11_TABLE_MOD, ofp_version, 0);
         otm = ofpbuf_put_zeros(b, sizeof *otm);
-        otm->table_id = pm->table_id;
-        otm->config = ofputil_table_miss_to_config(pm->miss_config,
-                                                   ofp_version);
+        otm->table_id = tm->table_id;
+        otm->config = ofputil_encode_table_config(tm->miss, tm->eviction,
+                                                  ofp_version);
         break;
     }
     case OFP14_VERSION:
     case OFP15_VERSION: {
         struct ofp14_table_mod *otm;
+        struct ofp14_table_mod_prop_eviction *ote;
 
         b = ofpraw_alloc(OFPRAW_OFPT14_TABLE_MOD, ofp_version, 0);
         otm = ofpbuf_put_zeros(b, sizeof *otm);
-        otm->table_id = pm->table_id;
-        otm->config = ofputil_table_miss_to_config(pm->miss_config,
-                                                   ofp_version);
+        otm->table_id = tm->table_id;
+        otm->config = ofputil_encode_table_config(tm->miss, tm->eviction,
+                                                  ofp_version);
+
+        if (tm->eviction_flags != UINT32_MAX) {
+            ote = ofpbuf_put_zeros(b, sizeof *ote);
+            ote->type = htons(OFPTMPT14_EVICTION);
+            ote->length = htons(sizeof *ote);
+            ote->flags = htonl(tm->eviction_flags);
+        }
         break;
     }
     default:
@@ -5390,8 +5444,9 @@ ofputil_put_ofp12_table_stats(const struct ofputil_table_stats *stats,
     out->metadata_write = features->metadata_write;
     out->instructions = ovsinst_bitmap_to_openflow(
         features->nonmiss.instructions, OFP12_VERSION);
-    out->config = ofputil_table_miss_to_config(features->miss_config,
-                                               OFP12_VERSION);
+    out->config = ofputil_encode_table_config(features->miss_config,
+                                              OFPUTIL_TABLE_EVICTION_DEFAULT,
+                                              OFP12_VERSION);
     out->max_entries = htonl(features->max_entries);
     out->active_count = htonl(stats->active_count);
     out->lookup_count = htonll(stats->lookup_count);
@@ -5497,8 +5552,8 @@ ofputil_decode_ofp11_table_stats(struct ofpbuf *msg,
     features->nonmiss.apply.ofpacts = ofpact_bitmap_from_openflow(
         ots->write_actions, OFP11_VERSION);
     features->miss = features->nonmiss;
-    features->miss_config = ofputil_table_miss_from_config(ots->config,
-                                                           OFP11_VERSION);
+    features->miss_config = ofputil_decode_table_miss(ots->config,
+                                                      OFP11_VERSION);
     features->match = mf_bitmap_from_of11(ots->match);
     features->wildcard = mf_bitmap_from_of11(ots->wildcards);
     bitmap_or(features->match.bm, features->wildcard.bm, MFF_N_IDS);
@@ -5527,8 +5582,8 @@ ofputil_decode_ofp12_table_stats(struct ofpbuf *msg,
     ovs_strlcpy(features->name, ots->name, sizeof features->name);
     features->metadata_match = ots->metadata_match;
     features->metadata_write = ots->metadata_write;
-    features->miss_config = ofputil_table_miss_from_config(ots->config,
-                                                           OFP12_VERSION);
+    features->miss_config = ofputil_decode_table_miss(ots->config,
+                                                      OFP12_VERSION);
     features->max_entries = ntohl(ots->max_entries);
 
     features->nonmiss.instructions = ovsinst_bitmap_from_openflow(
@@ -5596,6 +5651,8 @@ ofputil_decode_table_stats_reply(struct ofpbuf *msg,
 
     memset(stats, 0, sizeof *stats);
     memset(features, 0, sizeof *features);
+    features->supports_eviction = -1;
+    features->supports_vacancy_events = -1;
 
     switch ((enum ofp_version) oh->version) {
     case OFP10_VERSION:
@@ -6886,10 +6943,9 @@ ofputil_decode_port_stats_request(const struct ofp_header *request,
 void
 ofputil_bucket_list_destroy(struct ovs_list *buckets)
 {
-    struct ofputil_bucket *bucket, *next_bucket;
+    struct ofputil_bucket *bucket;
 
-    LIST_FOR_EACH_SAFE (bucket, next_bucket, list_node, buckets) {
-        list_remove(&bucket->list_node);
+    LIST_FOR_EACH_POP (bucket, list_node, buckets) {
         free(bucket->ofpacts);
         free(bucket);
     }
@@ -7427,6 +7483,26 @@ ofputil_put_ofp15_bucket(const struct ofputil_bucket *bucket,
     ob->bucket_id = htonl(bucket_id);
 }
 
+static void
+ofputil_put_group_prop_ntr_selection_method(enum ofp_version ofp_version,
+                                            const struct ofputil_group_props *gp,
+                                            struct ofpbuf *openflow)
+{
+    struct ntr_group_prop_selection_method *prop;
+    size_t start;
+
+    start = openflow->size;
+    ofpbuf_put_zeros(openflow, sizeof *prop);
+    oxm_put_field_array(openflow, &gp->fields, ofp_version);
+    prop = ofpbuf_at_assert(openflow, start, sizeof *prop);
+    prop->type = htons(OFPGPT15_EXPERIMENTER);
+    prop->experimenter = htonl(NTR_VENDOR_ID);
+    prop->exp_type = htonl(NTRT_SELECTION_METHOD);
+    strcpy(prop->selection_method, gp->selection_method);
+    prop->selection_method_param = htonll(gp->selection_method_param);
+    end_property(openflow, start);
+}
+
 static void
 ofputil_append_ofp11_group_desc_reply(const struct ofputil_group_desc *gds,
                                       const struct ovs_list *buckets,
@@ -7475,6 +7551,12 @@ ofputil_append_ofp15_group_desc_reply(const struct ofputil_group_desc *gds,
     ogds->group_id = htonl(gds->group_id);
     ogds->bucket_list_len =  htons(reply->size - start_buckets);
 
+    /* Add group properties */
+    if (gds->props.selection_method[0]) {
+        ofputil_put_group_prop_ntr_selection_method(version, &gds->props,
+                                                    reply);
+    }
+
     ofpmp_postappend(replies, start_ogds);
 }
 
@@ -7793,14 +7875,11 @@ parse_group_prop_ntr_selection_method(struct ofpbuf *payload,
         return OFPERR_OFPBPC_BAD_VALUE;
     }
 
-    /* Only allow selection method property if the selection_method field
-     * matches a suported method. As no methods are currently supported
-     * this check is a no-op that always fails. As selection methods are
-     * added they should be checked against the selection_method field
-     * here. */
-    log_property(false, "ntr selection method '%s' is not supported",
-                 prop->selection_method);
-    return OFPERR_OFPBPC_BAD_VALUE;
+    if (strcmp("hash", prop->selection_method)) {
+        log_property(false, "ntr selection method '%s' is not supported",
+                     prop->selection_method);
+        return OFPERR_OFPBPC_BAD_VALUE;
+    }
 
     strcpy(gp->selection_method, prop->selection_method);
     gp->selection_method_param = ntohll(prop->selection_method_param);
@@ -8138,12 +8217,18 @@ ofputil_encode_ofp15_group_mod(enum ofp_version ofp_version,
     ogm->command_bucket_id = htonl(gm->command_bucket_id);
     ogm->bucket_array_len = htons(b->size - start_ogm - sizeof *ogm);
 
+    /* Add group properties */
+    if (gm->props.selection_method[0]) {
+        ofputil_put_group_prop_ntr_selection_method(ofp_version, &gm->props, b);
+    }
+
     id_pool_destroy(bucket_ids);
     return b;
 }
 
 static void
-bad_group_cmd(enum ofp15_group_mod_command cmd) {
+bad_group_cmd(enum ofp15_group_mod_command cmd)
+{
     const char *opt_version;
     const char *version;
     const char *cmd_str;
@@ -8160,6 +8245,7 @@ bad_group_cmd(enum ofp15_group_mod_command cmd) {
     case OFPGC15_REMOVE_BUCKET:
         version = "1.5";
         opt_version = "15";
+        break;
 
     default:
         OVS_NOT_REACHED();
@@ -8183,7 +8269,7 @@ bad_group_cmd(enum ofp15_group_mod_command cmd) {
         break;
 
     case OFPGC15_REMOVE_BUCKET:
-        cmd_str = "insert-bucket";
+        cmd_str = "remove-bucket";
         break;
 
     default:
@@ -8346,7 +8432,7 @@ ofputil_decode_group_mod(const struct ofp_header *oh,
     switch (gm->type) {
     case OFPGT11_INDIRECT:
         if (!list_is_singleton(&gm->buckets)) {
-            return OFPERR_OFPGMFC_INVALID_GROUP;
+            return OFPERR_OFPGMFC_OUT_OF_BUCKETS;
         }
         break;
     case OFPGT11_ALL:
@@ -8718,6 +8804,36 @@ ofputil_decode_bundle_ctrl(const struct ofp_header *oh,
     return 0;
 }
 
+struct ofpbuf *
+ofputil_encode_bundle_ctrl_request(enum ofp_version ofp_version,
+                                   struct ofputil_bundle_ctrl_msg *bc)
+{
+    struct ofpbuf *request;
+    struct ofp14_bundle_ctrl_msg *m;
+
+    switch (ofp_version) {
+    case OFP10_VERSION:
+    case OFP11_VERSION:
+    case OFP12_VERSION:
+    case OFP13_VERSION:
+        ovs_fatal(0, "bundles need OpenFlow 1.4 or later "
+                     "(\'-O OpenFlow14\')");
+    case OFP14_VERSION:
+    case OFP15_VERSION:
+        request = ofpraw_alloc(OFPRAW_OFPT14_BUNDLE_CONTROL, ofp_version, 0);
+        m = ofpbuf_put_zeros(request, sizeof *m);
+
+        m->bundle_id = htonl(bc->bundle_id);
+        m->type = htons(bc->type);
+        m->flags = htons(bc->flags);
+        break;
+    default:
+        OVS_NOT_REACHED();
+    }
+
+    return request;
+}
+
 struct ofpbuf *
 ofputil_encode_bundle_ctrl_reply(const struct ofp_header *oh,
                                  struct ofputil_bundle_ctrl_msg *msg)
@@ -8752,6 +8868,7 @@ ofputil_is_bundlable(enum ofptype type)
     case OFPTYPE_TABLE_MOD:
     case OFPTYPE_METER_MOD:
     case OFPTYPE_PACKET_OUT:
+    case OFPTYPE_NXT_GENEVE_TABLE_MOD:
 
         /* Not to be bundlable. */
     case OFPTYPE_ECHO_REQUEST:
@@ -8815,6 +8932,8 @@ ofputil_is_bundlable(enum ofptype type)
     case OFPTYPE_METER_FEATURES_STATS_REPLY:
     case OFPTYPE_TABLE_FEATURES_STATS_REPLY:
     case OFPTYPE_ROLE_STATUS:
+    case OFPTYPE_NXT_GENEVE_TABLE_REQUEST:
+    case OFPTYPE_NXT_GENEVE_TABLE_REPLY:
         break;
     }
 
@@ -8823,7 +8942,8 @@ ofputil_is_bundlable(enum ofptype type)
 
 enum ofperr
 ofputil_decode_bundle_add(const struct ofp_header *oh,
-                          struct ofputil_bundle_add_msg *msg)
+                          struct ofputil_bundle_add_msg *msg,
+                          enum ofptype *type_ptr)
 {
     const struct ofp14_bundle_ctrl_msg *m;
     struct ofpbuf b;
@@ -8850,14 +8970,17 @@ ofputil_decode_bundle_add(const struct ofp_header *oh,
     }
 
     /* Reject unbundlable messages. */
-    error = ofptype_decode(&type, msg->msg);
+    if (!type_ptr) {
+        type_ptr = &type;
+    }
+    error = ofptype_decode(type_ptr, 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)) {
+    if (!ofputil_is_bundlable(*type_ptr)) {
         return OFPERR_OFPBFC_MSG_UNSUP;
     }
 
@@ -8871,7 +8994,9 @@ ofputil_encode_bundle_add(enum ofp_version ofp_version,
     struct ofpbuf *request;
     struct ofp14_bundle_ctrl_msg *m;
 
-    request = ofpraw_alloc(OFPRAW_OFPT14_BUNDLE_ADD_MESSAGE, ofp_version, 0);
+    /* Must use the same xid as the embedded message. */
+    request = ofpraw_alloc_xid(OFPRAW_OFPT14_BUNDLE_ADD_MESSAGE, ofp_version,
+                               msg->msg->xid, 0);
     m = ofpbuf_put_zeros(request, sizeof *m);
 
     m->bundle_id = htonl(msg->bundle_id);
@@ -8880,3 +9005,139 @@ ofputil_encode_bundle_add(enum ofp_version ofp_version,
 
     return request;
 }
+
+static void
+encode_geneve_table_mappings(struct ofpbuf *b, struct ovs_list *mappings)
+{
+    struct ofputil_geneve_map *map;
+
+    LIST_FOR_EACH (map, list_node, mappings) {
+        struct nx_geneve_map *nx_map;
+
+        nx_map = ofpbuf_put_zeros(b, sizeof *nx_map);
+        nx_map->option_class = htons(map->option_class);
+        nx_map->option_type = map->option_type;
+        nx_map->option_len = map->option_len;
+        nx_map->index = htons(map->index);
+    }
+}
+
+struct ofpbuf *
+ofputil_encode_geneve_table_mod(enum ofp_version ofp_version,
+                                struct ofputil_geneve_table_mod *gtm)
+{
+    struct ofpbuf *b;
+    struct nx_geneve_table_mod *nx_gtm;
+
+    b = ofpraw_alloc(OFPRAW_NXT_GENEVE_TABLE_MOD, ofp_version, 0);
+    nx_gtm = ofpbuf_put_zeros(b, sizeof *nx_gtm);
+    nx_gtm->command = htons(gtm->command);
+    encode_geneve_table_mappings(b, &gtm->mappings);
+
+    return b;
+}
+
+static enum ofperr
+decode_geneve_table_mappings(struct ofpbuf *msg, struct ovs_list *mappings)
+{
+    list_init(mappings);
+
+    while (msg->size) {
+        struct nx_geneve_map *nx_map;
+        struct ofputil_geneve_map *map;
+
+        nx_map = ofpbuf_pull(msg, sizeof *nx_map);
+        map = xmalloc(sizeof *map);
+        list_push_back(mappings, &map->list_node);
+
+        map->option_class = ntohs(nx_map->option_class);
+        map->option_type = nx_map->option_type;
+
+        map->option_len = nx_map->option_len;
+        if (map->option_len == 0 || map->option_len % 4 ||
+            map->option_len > GENEVE_MAX_OPT_SIZE) {
+            VLOG_WARN_RL(&bad_ofmsg_rl,
+                         "geneve table option length (%u) is not a valid option size",
+                         map->option_len);
+            ofputil_uninit_geneve_table(mappings);
+            return OFPERR_NXGTMFC_BAD_OPT_LEN;
+        }
+
+        map->index = ntohs(nx_map->index);
+        if (map->index >= TUN_METADATA_NUM_OPTS) {
+            VLOG_WARN_RL(&bad_ofmsg_rl,
+                         "geneve table field index (%u) is too large (max %u)",
+                         map->index, TUN_METADATA_NUM_OPTS - 1);
+            ofputil_uninit_geneve_table(mappings);
+            return OFPERR_NXGTMFC_BAD_FIELD_IDX;
+        }
+    }
+
+    return 0;
+}
+
+enum ofperr
+ofputil_decode_geneve_table_mod(const struct ofp_header *oh,
+                                struct ofputil_geneve_table_mod *gtm)
+{
+    struct ofpbuf msg;
+    struct nx_geneve_table_mod *nx_gtm;
+
+    ofpbuf_use_const(&msg, oh, ntohs(oh->length));
+    ofpraw_pull_assert(&msg);
+
+    nx_gtm = ofpbuf_pull(&msg, sizeof *nx_gtm);
+    gtm->command = ntohs(nx_gtm->command);
+    if (gtm->command > NXGTMC_CLEAR) {
+        VLOG_WARN_RL(&bad_ofmsg_rl,
+                     "geneve table mod command (%u) is out of range",
+                     gtm->command);
+        return OFPERR_NXGTMFC_BAD_COMMAND;
+    }
+
+    return decode_geneve_table_mappings(&msg, &gtm->mappings);
+}
+
+struct ofpbuf *
+ofputil_encode_geneve_table_reply(const struct ofp_header *oh,
+                                  struct ofputil_geneve_table_reply *gtr)
+{
+    struct ofpbuf *b;
+    struct nx_geneve_table_reply *nx_gtr;
+
+    b = ofpraw_alloc_reply(OFPRAW_NXT_GENEVE_TABLE_REPLY, oh, 0);
+    nx_gtr = ofpbuf_put_zeros(b, sizeof *nx_gtr);
+    nx_gtr->max_option_space = htonl(gtr->max_option_space);
+    nx_gtr->max_fields = htons(gtr->max_fields);
+
+    encode_geneve_table_mappings(b, &gtr->mappings);
+
+    return b;
+}
+
+enum ofperr
+ofputil_decode_geneve_table_reply(const struct ofp_header *oh,
+                                  struct ofputil_geneve_table_reply *gtr)
+{
+    struct ofpbuf msg;
+    struct nx_geneve_table_reply *nx_gtr;
+
+    ofpbuf_use_const(&msg, oh, ntohs(oh->length));
+    ofpraw_pull_assert(&msg);
+
+    nx_gtr = ofpbuf_pull(&msg, sizeof *nx_gtr);
+    gtr->max_option_space = ntohl(nx_gtr->max_option_space);
+    gtr->max_fields = ntohs(nx_gtr->max_fields);
+
+    return decode_geneve_table_mappings(&msg, &gtr->mappings);
+}
+
+void
+ofputil_uninit_geneve_table(struct ovs_list *mappings)
+{
+    struct ofputil_geneve_map *map;
+
+    LIST_FOR_EACH_POP (map, list_node, mappings) {
+        free(map);
+    }
+}