Implement Openflow 1.4 Vacancy Events for OFPT_TABLE_MOD.
[cascardo/ovs.git] / lib / ofp-util.c
index d02ee3a..207c335 100644 (file)
@@ -53,10 +53,13 @@ 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_vacancy ofputil_decode_table_vacancy(
+    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 ofputil_table_vacancy,
                                             enum ofp_version);
 
 struct ofp_prop_header {
@@ -199,7 +202,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 == 32);
+    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 34);
 
     /* Initialize most of wc. */
     flow_wildcards_init_catchall(wc);
@@ -228,10 +231,10 @@ ofputil_wildcard_from_ofpfw10(uint32_t ofpfw, struct flow_wildcards *wc)
     }
 
     if (!(ofpfw & OFPFW10_DL_SRC)) {
-        memset(wc->masks.dl_src, 0xff, ETH_ADDR_LEN);
+        WC_MASK_FIELD(wc, dl_src);
     }
     if (!(ofpfw & OFPFW10_DL_DST)) {
-        memset(wc->masks.dl_dst, 0xff, ETH_ADDR_LEN);
+        WC_MASK_FIELD(wc, dl_dst);
     }
     if (!(ofpfw & OFPFW10_DL_TYPE)) {
         wc->masks.dl_type = OVS_BE16_MAX;
@@ -264,8 +267,8 @@ ofputil_match_from_ofp10_match(const struct ofp10_match *ofmatch,
     match->flow.dl_type = ofputil_dl_type_from_openflow(ofmatch->dl_type);
     match->flow.tp_src = ofmatch->tp_src;
     match->flow.tp_dst = ofmatch->tp_dst;
-    memcpy(match->flow.dl_src, ofmatch->dl_src, ETH_ADDR_LEN);
-    memcpy(match->flow.dl_dst, ofmatch->dl_dst, ETH_ADDR_LEN);
+    match->flow.dl_src = ofmatch->dl_src;
+    match->flow.dl_dst = ofmatch->dl_dst;
     match->flow.nw_tos = ofmatch->nw_tos & IP_DSCP_MASK;
     match->flow.nw_proto = ofmatch->nw_proto;
 
@@ -344,7 +347,6 @@ ofputil_match_to_ofp10_match(const struct match *match,
     } else if (match->wc.masks.vlan_tci & htons(VLAN_CFI)
                && !(match->flow.vlan_tci & htons(VLAN_CFI))) {
         ofmatch->dl_vlan = htons(OFP10_VLAN_NONE);
-        ofpfw |= OFPFW10_DL_VLAN_PCP;
     } else {
         if (!(match->wc.masks.vlan_tci & htons(VLAN_VID_MASK))) {
             ofpfw |= OFPFW10_DL_VLAN;
@@ -362,8 +364,8 @@ ofputil_match_to_ofp10_match(const struct match *match,
     /* Compose most of the match structure. */
     ofmatch->wildcards = htonl(ofpfw);
     ofmatch->in_port = htons(ofp_to_u16(match->flow.in_port.ofp_port));
-    memcpy(ofmatch->dl_src, match->flow.dl_src, ETH_ADDR_LEN);
-    memcpy(ofmatch->dl_dst, match->flow.dl_dst, ETH_ADDR_LEN);
+    ofmatch->dl_src = match->flow.dl_src;
+    ofmatch->dl_dst = match->flow.dl_dst;
     ofmatch->dl_type = ofputil_dl_type_to_openflow(match->flow.dl_type);
     ofmatch->nw_src = match->flow.nw_src;
     ofmatch->nw_dst = match->flow.nw_dst;
@@ -420,10 +422,7 @@ ofputil_match_from_ofp11_match(const struct ofp11_match *ofmatch,
                                struct match *match)
 {
     uint16_t wc = ntohl(ofmatch->wildcards);
-    uint8_t dl_src_mask[ETH_ADDR_LEN];
-    uint8_t dl_dst_mask[ETH_ADDR_LEN];
     bool ipv4, arp, rarp;
-    int i;
 
     match_init_catchall(match);
 
@@ -438,15 +437,10 @@ ofputil_match_from_ofp11_match(const struct ofp11_match *ofmatch,
         match_set_in_port(match, ofp_port);
     }
 
-    for (i = 0; i < ETH_ADDR_LEN; i++) {
-        dl_src_mask[i] = ~ofmatch->dl_src_mask[i];
-    }
-    match_set_dl_src_masked(match, ofmatch->dl_src, dl_src_mask);
-
-    for (i = 0; i < ETH_ADDR_LEN; i++) {
-        dl_dst_mask[i] = ~ofmatch->dl_dst_mask[i];
-    }
-    match_set_dl_dst_masked(match, ofmatch->dl_dst, dl_dst_mask);
+    match_set_dl_src_masked(match, ofmatch->dl_src,
+                            eth_addr_invert(ofmatch->dl_src_mask));
+    match_set_dl_dst_masked(match, ofmatch->dl_dst,
+                            eth_addr_invert(ofmatch->dl_dst_mask));
 
     if (!(wc & OFPFW11_DL_VLAN)) {
         if (ofmatch->dl_vlan == htons(OFPVID11_NONE)) {
@@ -573,7 +567,6 @@ ofputil_match_to_ofp11_match(const struct match *match,
                              struct ofp11_match *ofmatch)
 {
     uint32_t wc = 0;
-    int i;
 
     memset(ofmatch, 0, sizeof *ofmatch);
     ofmatch->omh.type = htons(OFPMT_STANDARD);
@@ -585,15 +578,10 @@ ofputil_match_to_ofp11_match(const struct match *match,
         ofmatch->in_port = ofputil_port_to_ofp11(match->flow.in_port.ofp_port);
     }
 
-    memcpy(ofmatch->dl_src, match->flow.dl_src, ETH_ADDR_LEN);
-    for (i = 0; i < ETH_ADDR_LEN; i++) {
-        ofmatch->dl_src_mask[i] = ~match->wc.masks.dl_src[i];
-    }
-
-    memcpy(ofmatch->dl_dst, match->flow.dl_dst, ETH_ADDR_LEN);
-    for (i = 0; i < ETH_ADDR_LEN; i++) {
-        ofmatch->dl_dst_mask[i] = ~match->wc.masks.dl_dst[i];
-    }
+    ofmatch->dl_src = match->flow.dl_src;
+    ofmatch->dl_src_mask = eth_addr_invert(match->wc.masks.dl_src);
+    ofmatch->dl_dst = match->flow.dl_dst;
+    ofmatch->dl_dst_mask = eth_addr_invert(match->wc.masks.dl_dst);
 
     if (match->wc.masks.vlan_tci == htons(0)) {
         wc |= OFPFW11_DL_VLAN | OFPFW11_DL_VLAN_PCP;
@@ -1728,7 +1716,7 @@ ofputil_decode_flow_mod(struct ofputil_flow_mod *fm,
         fm->out_group = (ofm->command == OFPFC_DELETE ||
                          ofm->command == OFPFC_DELETE_STRICT
                          ? ntohl(ofm->out_group)
-                         : OFPG11_ANY);
+                         : OFPG_ANY);
         raw_flags = ofm->flags;
     } else {
         uint16_t command;
@@ -1760,7 +1748,7 @@ ofputil_decode_flow_mod(struct ofputil_flow_mod *fm,
             fm->importance = 0;
             fm->buffer_id = ntohl(ofm->buffer_id);
             fm->out_port = u16_to_ofp(ntohs(ofm->out_port));
-            fm->out_group = OFPG11_ANY;
+            fm->out_group = OFPG_ANY;
             raw_flags = ofm->flags;
         } else if (raw == OFPRAW_NXT_FLOW_MOD) {
             /* Nicira extended flow_mod. */
@@ -1788,7 +1776,7 @@ ofputil_decode_flow_mod(struct ofputil_flow_mod *fm,
             fm->importance = 0;
             fm->buffer_id = ntohl(nfm->buffer_id);
             fm->out_port = u16_to_ofp(ntohs(nfm->out_port));
-            fm->out_group = OFPG11_ANY;
+            fm->out_group = OFPG_ANY;
             raw_flags = nfm->flags;
         } else {
             OVS_NOT_REACHED();
@@ -2349,7 +2337,7 @@ ofputil_decode_ofpst10_flow_request(struct ofputil_flow_stats_request *fsr,
     fsr->aggregate = aggregate;
     ofputil_match_from_ofp10_match(&ofsr->match, &fsr->match);
     fsr->out_port = u16_to_ofp(ntohs(ofsr->out_port));
-    fsr->out_group = OFPG11_ANY;
+    fsr->out_group = OFPG_ANY;
     fsr->table_id = ofsr->table_id;
     fsr->cookie = fsr->cookie_mask = htonll(0);
 
@@ -2400,7 +2388,7 @@ ofputil_decode_nxst_flow_request(struct ofputil_flow_stats_request *fsr,
 
     fsr->aggregate = aggregate;
     fsr->out_port = u16_to_ofp(ntohs(nfsr->out_port));
-    fsr->out_group = OFPG11_ANY;
+    fsr->out_group = OFPG_ANY;
     fsr->table_id = nfsr->table_id;
 
     return 0;
@@ -3745,7 +3733,7 @@ ofputil_decode_ofp10_phy_port(struct ofputil_phy_port *pp,
                               const struct ofp10_phy_port *opp)
 {
     pp->port_no = u16_to_ofp(ntohs(opp->port_no));
-    memcpy(pp->hw_addr, opp->hw_addr, OFP_ETH_ALEN);
+    pp->hw_addr = opp->hw_addr;
     ovs_strlcpy(pp->name, opp->name, OFP_MAX_PORT_NAME_LEN);
 
     pp->config = ntohl(opp->config) & OFPPC10_ALL;
@@ -3772,7 +3760,7 @@ ofputil_decode_ofp11_port(struct ofputil_phy_port *pp,
     if (error) {
         return error;
     }
-    memcpy(pp->hw_addr, op->hw_addr, OFP_ETH_ALEN);
+    pp->hw_addr = op->hw_addr;
     ovs_strlcpy(pp->name, op->name, OFP_MAX_PORT_NAME_LEN);
 
     pp->config = ntohl(op->config) & OFPPC11_ALL;
@@ -3834,7 +3822,7 @@ ofputil_pull_ofp14_port(struct ofputil_phy_port *pp, struct ofpbuf *msg)
     if (error) {
         return error;
     }
-    memcpy(pp->hw_addr, op->hw_addr, OFP_ETH_ALEN);
+    pp->hw_addr = op->hw_addr;
     ovs_strlcpy(pp->name, op->name, OFP_MAX_PORT_NAME_LEN);
 
     pp->config = ntohl(op->config) & OFPPC11_ALL;
@@ -3876,7 +3864,7 @@ ofputil_encode_ofp10_phy_port(const struct ofputil_phy_port *pp,
     memset(opp, 0, sizeof *opp);
 
     opp->port_no = htons(ofp_to_u16(pp->port_no));
-    memcpy(opp->hw_addr, pp->hw_addr, ETH_ADDR_LEN);
+    opp->hw_addr = pp->hw_addr;
     ovs_strlcpy(opp->name, pp->name, OFP_MAX_PORT_NAME_LEN);
 
     opp->config = htonl(pp->config & OFPPC10_ALL);
@@ -3895,7 +3883,7 @@ ofputil_encode_ofp11_port(const struct ofputil_phy_port *pp,
     memset(op, 0, sizeof *op);
 
     op->port_no = ofputil_port_to_ofp11(pp->port_no);
-    memcpy(op->hw_addr, pp->hw_addr, ETH_ADDR_LEN);
+    op->hw_addr = pp->hw_addr;
     ovs_strlcpy(op->name, pp->name, OFP_MAX_PORT_NAME_LEN);
 
     op->config = htonl(pp->config & OFPPC11_ALL);
@@ -3922,7 +3910,7 @@ ofputil_put_ofp14_port(const struct ofputil_phy_port *pp,
     op = ofpbuf_put_zeros(b, sizeof *op);
     op->port_no = ofputil_port_to_ofp11(pp->port_no);
     op->length = htons(sizeof *op + sizeof *eth);
-    memcpy(op->hw_addr, pp->hw_addr, ETH_ADDR_LEN);
+    op->hw_addr = pp->hw_addr;
     ovs_strlcpy(op->name, pp->name, sizeof op->name);
     op->config = htonl(pp->config & OFPPC11_ALL);
     op->state = htonl(pp->state & OFPPS11_ALL);
@@ -3994,7 +3982,6 @@ ofputil_encode_port_desc_stats_request(enum ofp_version ofp_version,
                                        ofp_port_t port)
 {
     struct ofpbuf *request;
-    ovs_be32 ofp11_port;
 
     switch (ofp_version) {
     case OFP10_VERSION:
@@ -4005,14 +3992,14 @@ ofputil_encode_port_desc_stats_request(enum ofp_version ofp_version,
         request = ofpraw_alloc(OFPRAW_OFPST10_PORT_DESC_REQUEST,
                                ofp_version, 0);
         break;
-
-    case OFP15_VERSION:
+    case OFP15_VERSION:{
+        struct ofp15_port_desc_request *req;
         request = ofpraw_alloc(OFPRAW_OFPST15_PORT_DESC_REQUEST,
                                ofp_version, 0);
-        ofp11_port = ofputil_port_to_ofp11(port);
-        ofpbuf_put(request, &ofp11_port, sizeof ofp11_port);
+        req = ofpbuf_put_zeros(request, sizeof *req);
+        req->port_no = ofputil_port_to_ofp11(port);
         break;
-
+    }
     default:
         OVS_NOT_REACHED();
     }
@@ -4335,7 +4322,7 @@ ofputil_decode_port_mod(const struct ofp_header *oh,
         const struct ofp10_port_mod *opm = b.data;
 
         pm->port_no = u16_to_ofp(ntohs(opm->port_no));
-        memcpy(pm->hw_addr, opm->hw_addr, ETH_ADDR_LEN);
+        pm->hw_addr = opm->hw_addr;
         pm->config = ntohl(opm->config) & OFPPC10_ALL;
         pm->mask = ntohl(opm->mask) & OFPPC10_ALL;
         pm->advertise = netdev_port_features_from_ofp10(opm->advertise);
@@ -4348,7 +4335,7 @@ ofputil_decode_port_mod(const struct ofp_header *oh,
             return error;
         }
 
-        memcpy(pm->hw_addr, opm->hw_addr, ETH_ADDR_LEN);
+        pm->hw_addr = opm->hw_addr;
         pm->config = ntohl(opm->config) & OFPPC11_ALL;
         pm->mask = ntohl(opm->mask) & OFPPC11_ALL;
         pm->advertise = netdev_port_features_from_ofp11(opm->advertise);
@@ -4363,7 +4350,7 @@ ofputil_decode_port_mod(const struct ofp_header *oh,
             return error;
         }
 
-        memcpy(pm->hw_addr, opm->hw_addr, ETH_ADDR_LEN);
+        pm->hw_addr = opm->hw_addr;
         pm->config = ntohl(opm->config) & OFPPC11_ALL;
         pm->mask = ntohl(opm->mask) & OFPPC11_ALL;
 
@@ -4423,7 +4410,7 @@ ofputil_encode_port_mod(const struct ofputil_port_mod *pm,
         b = ofpraw_alloc(OFPRAW_OFPT10_PORT_MOD, ofp_version, 0);
         opm = ofpbuf_put_zeros(b, sizeof *opm);
         opm->port_no = htons(ofp_to_u16(pm->port_no));
-        memcpy(opm->hw_addr, pm->hw_addr, ETH_ADDR_LEN);
+        opm->hw_addr = pm->hw_addr;
         opm->config = htonl(pm->config & OFPPC10_ALL);
         opm->mask = htonl(pm->mask & OFPPC10_ALL);
         opm->advertise = netdev_port_features_to_ofp10(pm->advertise);
@@ -4438,7 +4425,7 @@ ofputil_encode_port_mod(const struct ofputil_port_mod *pm,
         b = ofpraw_alloc(OFPRAW_OFPT11_PORT_MOD, ofp_version, 0);
         opm = ofpbuf_put_zeros(b, sizeof *opm);
         opm->port_no = ofputil_port_to_ofp11(pm->port_no);
-        memcpy(opm->hw_addr, pm->hw_addr, ETH_ADDR_LEN);
+        opm->hw_addr = pm->hw_addr;
         opm->config = htonl(pm->config & OFPPC11_ALL);
         opm->mask = htonl(pm->mask & OFPPC11_ALL);
         opm->advertise = netdev_port_features_to_ofp11(pm->advertise);
@@ -4452,7 +4439,7 @@ ofputil_encode_port_mod(const struct ofputil_port_mod *pm,
         b = ofpraw_alloc(OFPRAW_OFPT14_PORT_MOD, ofp_version, sizeof *eth);
         opm = ofpbuf_put_zeros(b, sizeof *opm);
         opm->port_no = ofputil_port_to_ofp11(pm->port_no);
-        memcpy(opm->hw_addr, pm->hw_addr, ETH_ADDR_LEN);
+        opm->hw_addr = pm->hw_addr;
         opm->config = htonl(pm->config & OFPPC11_ALL);
         opm->mask = htonl(pm->mask & OFPPC11_ALL);
 
@@ -5015,10 +5002,62 @@ ofputil_append_table_desc_reply(const struct ofputil_table_desc *td,
     otd->length = htons(reply->size - start_otd);
     otd->table_id = td->table_id;
     otd->config = ofputil_encode_table_config(OFPUTIL_TABLE_MISS_DEFAULT,
-                                              td->eviction, version);
+                                              td->eviction, td->vacancy,
+                                              version);
     ofpmp_postappend(replies, start_otd);
 }
 
+/* This function parses Vacancy property, and decodes the
+ * ofp14_table_mod_prop_vacancy in ofputil_table_mod.
+ * Returns OFPERR_OFPBPC_BAD_VALUE error code when vacancy_down is
+ * greater than vacancy_up and also when current vacancy has non-zero
+ * value. Returns 0 on success. */
+static enum ofperr
+parse_table_mod_vacancy_property(struct ofpbuf *property,
+                                 struct ofputil_table_mod *tm)
+{
+    struct ofp14_table_mod_prop_vacancy *otv = property->data;
+
+    if (property->size != sizeof *otv) {
+        return OFPERR_OFPBPC_BAD_LEN;
+    }
+    tm->table_vacancy.vacancy_down = otv->vacancy_down;
+    tm->table_vacancy.vacancy_up = otv->vacancy_up;
+    if (tm->table_vacancy.vacancy_down > tm->table_vacancy.vacancy_up) {
+        log_property(false, "Value of vacancy_down is greater than "
+                     "vacancy_up");
+        return OFPERR_OFPBPC_BAD_VALUE;
+    }
+    if (tm->table_vacancy.vacancy_down > 100 ||
+        tm->table_vacancy.vacancy_up > 100) {
+        log_property(false, "Vacancy threshold percentage should not be"
+                     "greater than 100");
+        return OFPERR_OFPBPC_BAD_VALUE;
+    }
+    tm->table_vacancy.vacancy = otv->vacancy;
+    if (tm->table_vacancy.vacancy) {
+        log_property(false, "Vacancy value should be zero for table-mod "
+                     "messages");
+        return OFPERR_OFPBPC_BAD_VALUE;
+    }
+    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 vacancy configuration that it specifies.
+ *
+ * Only OpenFlow 1.4 and later specify table vacancy configuration this way,
+ * so for other 'version' this function always returns
+ * OFPUTIL_TABLE_VACANCY_DEFAULT. */
+static enum ofputil_table_vacancy
+ofputil_decode_table_vacancy(ovs_be32 config, enum ofp_version version)
+{
+    return (version < OFP14_VERSION ? OFPUTIL_TABLE_VACANCY_DEFAULT
+            : config & htonl(OFPTC14_VACANCY_EVENTS) ? OFPUTIL_TABLE_VACANCY_ON
+            : OFPUTIL_TABLE_VACANCY_OFF);
+}
+
 static enum ofperr
 parse_table_mod_eviction_property(struct ofpbuf *property,
                                   struct ofputil_table_mod *tm)
@@ -5054,8 +5093,10 @@ ofputil_decode_table_eviction(ovs_be32 config, enum ofp_version version)
 static ovs_be32
 ofputil_encode_table_config(enum ofputil_table_miss miss,
                             enum ofputil_table_eviction eviction,
+                            enum ofputil_table_vacancy vacancy,
                             enum ofp_version version)
 {
+    uint32_t config = 0;
     /* See the section "OFPTC_* Table Configuration" in DESIGN.md for more
      * information on the crazy evolution of this field. */
     switch (version) {
@@ -5088,10 +5129,15 @@ ofputil_encode_table_config(enum ofputil_table_miss miss,
 
     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);
+        /* OpenFlow 1.4 introduced OFPTC14_EVICTION and
+         * OFPTC14_VACANCY_EVENTS. */
+        if (eviction == OFPUTIL_TABLE_EVICTION_ON) {
+            config |= OFPTC14_EVICTION;
+        }
+        if (vacancy == OFPUTIL_TABLE_VACANCY_ON) {
+            config |= OFPTC14_VACANCY_EVENTS;
+        }
+        return htonl(config);
     }
 
     OVS_NOT_REACHED();
@@ -5142,6 +5188,7 @@ ofputil_decode_table_mod(const struct ofp_header *oh,
     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);
 
@@ -5156,6 +5203,7 @@ ofputil_decode_table_mod(const struct ofp_header *oh,
         pm->table_id = otm->table_id;
         pm->miss = ofputil_decode_table_miss(otm->config, oh->version);
         pm->eviction = ofputil_decode_table_eviction(otm->config, oh->version);
+        pm->vacancy = ofputil_decode_table_vacancy(otm->config, oh->version);
         while (b.size > 0) {
             struct ofpbuf property;
             enum ofperr error;
@@ -5171,6 +5219,10 @@ ofputil_decode_table_mod(const struct ofp_header *oh,
                 error = parse_table_mod_eviction_property(&property, pm);
                 break;
 
+            case OFPTMPT14_VACANCY:
+                error = parse_table_mod_vacancy_property(&property, pm);
+                break;
+
             default:
                 error = OFPERR_OFPBRC_BAD_TYPE;
                 break;
@@ -5212,19 +5264,20 @@ ofputil_encode_table_mod(const struct ofputil_table_mod *tm,
         otm = ofpbuf_put_zeros(b, sizeof *otm);
         otm->table_id = tm->table_id;
         otm->config = ofputil_encode_table_config(tm->miss, tm->eviction,
-                                                  ofp_version);
+                                                  tm->vacancy, ofp_version);
         break;
     }
     case OFP14_VERSION:
     case OFP15_VERSION: {
         struct ofp14_table_mod *otm;
         struct ofp14_table_mod_prop_eviction *ote;
+        struct ofp14_table_mod_prop_vacancy *otv;
 
         b = ofpraw_alloc(OFPRAW_OFPT14_TABLE_MOD, ofp_version, 0);
         otm = ofpbuf_put_zeros(b, sizeof *otm);
         otm->table_id = tm->table_id;
         otm->config = ofputil_encode_table_config(tm->miss, tm->eviction,
-                                                  ofp_version);
+                                                  tm->vacancy, ofp_version);
 
         if (tm->eviction_flags != UINT32_MAX) {
             ote = ofpbuf_put_zeros(b, sizeof *ote);
@@ -5232,6 +5285,13 @@ ofputil_encode_table_mod(const struct ofputil_table_mod *tm,
             ote->length = htons(sizeof *ote);
             ote->flags = htonl(tm->eviction_flags);
         }
+        if (tm->vacancy == OFPUTIL_TABLE_VACANCY_ON) {
+            otv = ofpbuf_put_zeros(b, sizeof *otv);
+            otv->type = htons(OFPTMPT14_VACANCY);
+            otv->length = htons(sizeof *otv);
+            otv->vacancy_down = tm->table_vacancy.vacancy_down;
+            otv->vacancy_up = tm->table_vacancy.vacancy_up;
+        }
         break;
     }
     default:
@@ -5392,6 +5452,125 @@ ofputil_decode_role_status(const struct ofp_header *oh,
     return 0;
 }
 
+/* Encodes 'rf' according to 'protocol', and returns the encoded message.
+ * 'protocol' must be for OpenFlow 1.4 or later. */
+struct ofpbuf *
+ofputil_encode_requestforward(const struct ofputil_requestforward *rf,
+                              enum ofputil_protocol protocol)
+{
+    enum ofp_version ofp_version = ofputil_protocol_to_ofp_version(protocol);
+    struct ofpbuf *inner;
+
+    switch (rf->reason) {
+    case OFPRFR_GROUP_MOD:
+        inner = ofputil_encode_group_mod(ofp_version, rf->group_mod);
+        break;
+
+    case OFPRFR_METER_MOD:
+        inner = ofputil_encode_meter_mod(ofp_version, rf->meter_mod);
+        break;
+
+    default:
+        OVS_NOT_REACHED();
+    }
+
+    struct ofp_header *inner_oh = inner->data;
+    inner_oh->xid = rf->xid;
+    inner_oh->length = htons(inner->size);
+
+    struct ofpbuf *outer = ofpraw_alloc_xid(OFPRAW_OFPT14_REQUESTFORWARD,
+                                            ofp_version, htonl(0),
+                                            inner->size);
+    ofpbuf_put(outer, inner->data, inner->size);
+    ofpbuf_delete(inner);
+
+    return outer;
+}
+
+/* Decodes OFPT_REQUESTFORWARD message 'outer'.  On success, puts the decoded
+ * form into '*rf' and returns 0, and the caller is later responsible for
+ * freeing the content of 'rf', with ofputil_destroy_requestforward(rf).  On
+ * failure, returns an ofperr and '*rf' is indeterminate. */
+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));
+
+    /* Skip past outer message. */
+    enum ofpraw outer_raw = ofpraw_pull_assert(&b);
+    ovs_assert(outer_raw == OFPRAW_OFPT14_REQUESTFORWARD);
+
+    /* Validate inner message. */
+    if (b.size < sizeof(struct ofp_header)) {
+        return OFPERR_OFPBFC_MSG_BAD_LEN;
+    }
+    const struct ofp_header *inner = b.data;
+    unsigned int inner_len = ntohs(inner->length);
+    if (inner_len < sizeof(struct ofp_header) || inner_len > b.size) {
+        return OFPERR_OFPBFC_MSG_BAD_LEN;
+    }
+    if (inner->version != outer->version) {
+        return OFPERR_OFPBRC_BAD_VERSION;
+    }
+
+    /* Parse inner message. */
+    enum ofptype type;
+    error = ofptype_decode(&type, inner);
+    if (error) {
+        return error;
+    }
+
+    rf->xid = inner->xid;
+    if (type == OFPTYPE_GROUP_MOD) {
+        rf->reason = OFPRFR_GROUP_MOD;
+        rf->group_mod = xmalloc(sizeof *rf->group_mod);
+        error = ofputil_decode_group_mod(inner, rf->group_mod);
+        if (error) {
+            free(rf->group_mod);
+            return error;
+        }
+    } else if (type == OFPTYPE_METER_MOD) {
+        rf->reason = OFPRFR_METER_MOD;
+        rf->meter_mod = xmalloc(sizeof *rf->meter_mod);
+        ofpbuf_init(&rf->bands, 64);
+        error = ofputil_decode_meter_mod(inner, rf->meter_mod, &rf->bands);
+        if (error) {
+            free(rf->meter_mod);
+            ofpbuf_uninit(&rf->bands);
+            return error;
+        }
+    } else {
+        return OFPERR_OFPBFC_MSG_UNSUP;
+    }
+
+    return 0;
+}
+
+/* Frees the content of 'rf', which should have been initialized through a
+ * successful call to ofputil_decode_requestforward(). */
+void
+ofputil_destroy_requestforward(struct ofputil_requestforward *rf)
+{
+    if (!rf) {
+        return;
+    }
+
+    switch (rf->reason) {
+    case OFPRFR_GROUP_MOD:
+        ofputil_uninit_group_mod(rf->group_mod);
+        free(rf->group_mod);
+        break;
+
+    case OFPRFR_METER_MOD:
+        ofpbuf_uninit(&rf->bands);
+        free(rf->meter_mod);
+    }
+}
+
 /* Table stats. */
 
 /* OpenFlow 1.0 and 1.1 don't distinguish between a field that cannot be
@@ -5579,6 +5758,7 @@ ofputil_put_ofp12_table_stats(const struct ofputil_table_stats *stats,
         features->nonmiss.instructions, OFP12_VERSION);
     out->config = ofputil_encode_table_config(features->miss_config,
                                               OFPUTIL_TABLE_EVICTION_DEFAULT,
+                                              OFPUTIL_TABLE_VACANCY_DEFAULT,
                                               OFP12_VERSION);
     out->max_entries = htonl(features->max_entries);
     out->active_count = htonl(stats->active_count);
@@ -6379,9 +6559,9 @@ bool
 ofputil_group_from_string(const char *s, uint32_t *group_idp)
 {
     if (!strcasecmp(s, "any")) {
-        *group_idp = OFPG11_ANY;
+        *group_idp = OFPG_ANY;
     } else if (!strcasecmp(s, "all")) {
-        *group_idp = OFPG11_ALL;
+        *group_idp = OFPG_ALL;
     } else if (!str_to_uint(s, 10, group_idp)) {
         VLOG_WARN("%s is not a valid group ID.  (Valid group IDs are "
                   "32-bit nonnegative integers or the keywords ANY or "
@@ -6394,7 +6574,7 @@ ofputil_group_from_string(const char *s, uint32_t *group_idp)
 
 /* Appends to 's' a string representation of the OpenFlow group ID 'group_id'.
  * Most groups' string representation is just the number, but for special
- * groups, e.g. OFPG11_ALL, it is the name, e.g. "ALL". */
+ * groups, e.g. OFPG_ALL, it is the name, e.g. "ALL". */
 void
 ofputil_format_group(uint32_t group_id, struct ds *s)
 {
@@ -6407,18 +6587,18 @@ ofputil_format_group(uint32_t group_id, struct ds *s)
 
 /* Puts in the 'bufsize' byte in 'namebuf' a null-terminated string
  * representation of OpenFlow group ID 'group_id'.  Most group are represented
- * as just their number, but special groups, e.g. OFPG11_ALL, are represented
+ * as just their number, but special groups, e.g. OFPG_ALL, are represented
  * by name, e.g. "ALL". */
 void
 ofputil_group_to_string(uint32_t group_id,
                         char namebuf[MAX_GROUP_NAME_LEN + 1], size_t bufsize)
 {
     switch (group_id) {
-    case OFPG11_ALL:
+    case OFPG_ALL:
         ovs_strlcpy(namebuf, "ALL", bufsize);
         break;
 
-    case OFPG11_ANY:
+    case OFPG_ANY:
         ovs_strlcpy(namebuf, "ANY", bufsize);
         break;
 
@@ -6523,10 +6703,10 @@ ofputil_normalize_match__(struct match *match, bool may_log)
         wc.masks.nw_ttl = 0;
     }
     if (!(may_match & MAY_ARP_SHA)) {
-        memset(wc.masks.arp_sha, 0, ETH_ADDR_LEN);
+        WC_UNMASK_FIELD(&wc, arp_sha);
     }
     if (!(may_match & MAY_ARP_THA)) {
-        memset(wc.masks.arp_tha, 0, ETH_ADDR_LEN);
+        WC_UNMASK_FIELD(&wc, arp_tha);
     }
     if (!(may_match & MAY_IPV6)) {
         wc.masks.ipv6_src = wc.masks.ipv6_dst = in6addr_any;
@@ -7258,7 +7438,6 @@ ofputil_encode_group_desc_request(enum ofp_version ofp_version,
                                   uint32_t group_id)
 {
     struct ofpbuf *request;
-    ovs_be32 gid;
 
     switch (ofp_version) {
     case OFP10_VERSION:
@@ -7271,12 +7450,14 @@ ofputil_encode_group_desc_request(enum ofp_version ofp_version,
         request = ofpraw_alloc(OFPRAW_OFPST11_GROUP_DESC_REQUEST,
                                ofp_version, 0);
         break;
-    case OFP15_VERSION:
+    case OFP15_VERSION:{
+        struct ofp15_group_desc_request *req;
         request = ofpraw_alloc(OFPRAW_OFPST15_GROUP_DESC_REQUEST,
                                ofp_version, 0);
-        gid = htonl(group_id);
-        ofpbuf_put(request, &gid, sizeof gid);
+        req = ofpbuf_put_zeros(request, sizeof *req);
+        req->group_id = htonl(group_id);
         break;
+    }
     default:
         OVS_NOT_REACHED();
     }
@@ -7679,7 +7860,6 @@ ofputil_append_ofp15_group_desc_reply(const struct ofputil_group_desc *gds,
                                  gds->type, reply, version);
     }
     ogds = ofpbuf_at_assert(reply, start_ogds, sizeof *ogds);
-    ogds->length = htons(reply->size - start_ogds);
     ogds->type = gds->type;
     ogds->group_id = htonl(gds->group_id);
     ogds->bucket_list_len =  htons(reply->size - start_buckets);
@@ -7689,6 +7869,7 @@ ofputil_append_ofp15_group_desc_reply(const struct ofputil_group_desc *gds,
         ofputil_put_group_prop_ntr_selection_method(version, &gds->props,
                                                     reply);
     }
+    ogds->length = htons(reply->size - start_ogds);
 
     ofpmp_postappend(replies, start_ogds);
 }
@@ -7822,7 +8003,8 @@ parse_ofp15_group_bucket_prop_watch(const struct ofpbuf *payload,
 
 static enum ofperr
 ofputil_pull_ofp15_buckets(struct ofpbuf *msg, size_t buckets_length,
-                           enum ofp_version version, struct ovs_list *buckets)
+                           enum ofp_version version, uint8_t group_type,
+                           struct ovs_list *buckets)
 {
     struct ofp15_bucket *ob;
 
@@ -7835,7 +8017,7 @@ ofputil_pull_ofp15_buckets(struct ofpbuf *msg, size_t buckets_length,
         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);
-        ovs_be16 weight = htons(1);
+        ovs_be16 weight = htons(group_type == OFPGT11_SELECT ? 1 : 0);
 
         ofpbuf_init(&ofpacts, 0);
 
@@ -8086,6 +8268,7 @@ parse_ofp15_group_prop_exp(struct ofpbuf *payload,
 
     switch (experimenter) {
     case NTR_VENDOR_ID:
+    case NTR_COMPAT_VENDOR_ID:
         error = parse_group_prop_ntr(payload, exp_type, group_type,
                                      group_cmd, gp);
         break;
@@ -8217,7 +8400,7 @@ ofputil_decode_ofp15_group_desc_reply(struct ofputil_group_desc *gd,
                      "bucket list length %u", bucket_list_len);
         return OFPERR_OFPBRC_BAD_LEN;
     }
-    error = ofputil_pull_ofp15_buckets(msg, bucket_list_len, version,
+    error = ofputil_pull_ofp15_buckets(msg, bucket_list_len, version, gd->type,
                                        &gd->buckets);
     if (error) {
         return error;
@@ -8232,7 +8415,7 @@ ofputil_decode_ofp15_group_desc_reply(struct ofputil_group_desc *gd,
      * claim that the group mod command is OFPGC15_ADD to
      * satisfy the check in parse_group_prop_ntr_selection_method() */
     return parse_ofp15_group_properties(msg, gd->type, OFPGC15_ADD, &gd->props,
-                                        msg->size);
+                                        length - sizeof *ogds - bucket_list_len);
 }
 
 /* Converts a group description reply in 'msg' into an abstract
@@ -8515,7 +8698,7 @@ ofputil_pull_ofp15_group_mod(struct ofpbuf *msg, enum ofp_version ofp_version,
 
     bucket_list_len = ntohs(ogm->bucket_array_len);
     error = ofputil_pull_ofp15_buckets(msg, bucket_list_len, ofp_version,
-                                       &gm->buckets);
+                                       gm->type, &gm->buckets);
     if (error) {
         return error;
     }
@@ -8565,7 +8748,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_OUT_OF_BUCKETS;
+            return OFPERR_OFPGMFC_INVALID_GROUP;
         }
         break;
     case OFPGT11_ALL:
@@ -8573,7 +8756,7 @@ ofputil_decode_group_mod(const struct ofp_header *oh,
     case OFPGT11_FF:
         break;
     default:
-        OVS_NOT_REACHED();
+        return OFPERR_OFPGMFC_BAD_TYPE;
     }
 
     switch (gm->command) {
@@ -8588,10 +8771,14 @@ ofputil_decode_group_mod(const struct ofp_header *oh,
         }
         break;
     default:
-        OVS_NOT_REACHED();
+        return OFPERR_OFPGMFC_BAD_COMMAND;
     }
 
     LIST_FOR_EACH (bucket, list_node, &gm->buckets) {
+        if (bucket->weight && gm->type != OFPGT11_SELECT) {
+            return OFPERR_OFPGMFC_INVALID_GROUP;
+        }
+
         switch (gm->type) {
         case OFPGT11_ALL:
         case OFPGT11_INDIRECT:
@@ -9067,6 +9254,7 @@ ofputil_is_bundlable(enum ofptype type)
     case OFPTYPE_TABLE_FEATURES_STATS_REPLY:
     case OFPTYPE_TABLE_DESC_REPLY:
     case OFPTYPE_ROLE_STATUS:
+    case OFPTYPE_REQUESTFORWARD:
     case OFPTYPE_NXT_GENEVE_TABLE_REQUEST:
     case OFPTYPE_NXT_GENEVE_TABLE_REPLY:
         break;
@@ -9119,6 +9307,8 @@ ofputil_decode_bundle_add(const struct ofp_header *oh,
     }
 
     if (!ofputil_is_bundlable(*type_ptr)) {
+        VLOG_WARN_RL(&bad_ofmsg_rl, "%s message not allowed inside "
+                     "OFPT14_BUNDLE_ADD_MESSAGE", ofptype_get_name(*type_ptr));
         return OFPERR_OFPBFC_MSG_UNSUP;
     }
 
@@ -9176,7 +9366,8 @@ ofputil_encode_geneve_table_mod(enum ofp_version ofp_version,
 }
 
 static enum ofperr
-decode_geneve_table_mappings(struct ofpbuf *msg, struct ovs_list *mappings)
+decode_geneve_table_mappings(struct ofpbuf *msg, unsigned int max_fields,
+                             struct ovs_list *mappings)
 {
     list_init(mappings);
 
@@ -9192,8 +9383,7 @@ decode_geneve_table_mappings(struct ofpbuf *msg, struct ovs_list *mappings)
         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) {
+        if (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);
@@ -9202,10 +9392,10 @@ decode_geneve_table_mappings(struct ofpbuf *msg, struct ovs_list *mappings)
         }
 
         map->index = ntohs(nx_map->index);
-        if (map->index >= TUN_METADATA_NUM_OPTS) {
+        if (map->index >= max_fields) {
             VLOG_WARN_RL(&bad_ofmsg_rl,
                          "geneve table field index (%u) is too large (max %u)",
-                         map->index, TUN_METADATA_NUM_OPTS - 1);
+                         map->index, max_fields - 1);
             ofputil_uninit_geneve_table(mappings);
             return OFPERR_NXGTMFC_BAD_FIELD_IDX;
         }
@@ -9233,7 +9423,8 @@ ofputil_decode_geneve_table_mod(const struct ofp_header *oh,
         return OFPERR_NXGTMFC_BAD_COMMAND;
     }
 
-    return decode_geneve_table_mappings(&msg, &gtm->mappings);
+    return decode_geneve_table_mappings(&msg, TUN_METADATA_NUM_OPTS,
+                                        &gtm->mappings);
 }
 
 struct ofpbuf *
@@ -9253,6 +9444,12 @@ ofputil_encode_geneve_table_reply(const struct ofp_header *oh,
     return b;
 }
 
+/* Decodes the NXT_GENEVE_TABLE_REPLY message in 'oh' into '*gtr'.  Returns 0
+ * if successful, otherwise an ofperr.
+ *
+ * The decoder verifies that the indexes in 'gtr->mappings' are less than
+ * 'gtr->max_fields', but the caller must ensure, if necessary, that they are
+ * less than TUN_METADATA_NUM_OPTS. */
 enum ofperr
 ofputil_decode_geneve_table_reply(const struct ofp_header *oh,
                                   struct ofputil_geneve_table_reply *gtr)
@@ -9267,7 +9464,7 @@ ofputil_decode_geneve_table_reply(const struct ofp_header *oh,
     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);
+    return decode_geneve_table_mappings(&msg, gtr->max_fields, &gtr->mappings);
 }
 
 void
@@ -9279,3 +9476,210 @@ ofputil_uninit_geneve_table(struct ovs_list *mappings)
         free(map);
     }
 }
+
+/* Decodes the OpenFlow "set async config" request and "get async config
+ * reply" message in '*oh' into an abstract form in 'master' and 'slave'.
+ *
+ * If 'loose' is true, this function ignores properties and values that it does
+ * not understand, as a controller would want to do when interpreting
+ * capabilities provided by a switch.  If 'loose' is false, this function
+ * treats unknown properties and values as an error, as a switch would want to
+ * do when interpreting a configuration request made by a controller.
+ *
+ * Returns 0 if successful, otherwise an OFPERR_* value. */
+enum ofperr
+ofputil_decode_set_async_config(const struct ofp_header *oh,
+                                uint32_t master[OAM_N_TYPES],
+                                uint32_t slave[OAM_N_TYPES],
+                                bool loose)
+{
+    enum ofpraw raw;
+    struct ofpbuf b;
+
+    ofpbuf_use_const(&b, oh, ntohs(oh->length));
+    raw = ofpraw_pull_assert(&b);
+
+    if (raw == OFPRAW_OFPT13_SET_ASYNC ||
+        raw == OFPRAW_NXT_SET_ASYNC_CONFIG ||
+        raw == OFPRAW_OFPT13_GET_ASYNC_REPLY) {
+        const struct nx_async_config *msg = ofpmsg_body(oh);
+
+        master[OAM_PACKET_IN] = ntohl(msg->packet_in_mask[0]);
+        master[OAM_PORT_STATUS] = ntohl(msg->port_status_mask[0]);
+        master[OAM_FLOW_REMOVED] = ntohl(msg->flow_removed_mask[0]);
+
+        slave[OAM_PACKET_IN] = ntohl(msg->packet_in_mask[1]);
+        slave[OAM_PORT_STATUS] = ntohl(msg->port_status_mask[1]);
+        slave[OAM_FLOW_REMOVED] = ntohl(msg->flow_removed_mask[1]);
+
+    } else if (raw == OFPRAW_OFPT14_SET_ASYNC ||
+               raw == OFPRAW_OFPT14_GET_ASYNC_REPLY) {
+
+        while (b.size > 0) {
+            struct ofp14_async_config_prop_reasons *msg;
+            struct ofpbuf property;
+            enum ofperr error;
+            uint16_t type;
+
+            error = ofputil_pull_property(&b, &property, &type);
+            if (error) {
+                return error;
+            }
+
+            msg = property.data;
+
+            if (property.size != sizeof *msg) {
+                return OFPERR_OFPBRC_BAD_LEN;
+            }
+
+            switch (type) {
+            case OFPACPT_PACKET_IN_SLAVE:
+                slave[OAM_PACKET_IN] = ntohl(msg->mask);
+                break;
+
+            case OFPACPT_PACKET_IN_MASTER:
+                master[OAM_PACKET_IN] = ntohl(msg->mask);
+                break;
+
+            case OFPACPT_PORT_STATUS_SLAVE:
+                slave[OAM_PORT_STATUS] = ntohl(msg->mask);
+                break;
+
+            case OFPACPT_PORT_STATUS_MASTER:
+                master[OAM_PORT_STATUS] = ntohl(msg->mask);
+                break;
+
+            case OFPACPT_FLOW_REMOVED_SLAVE:
+                slave[OAM_FLOW_REMOVED] = ntohl(msg->mask);
+                break;
+
+            case OFPACPT_FLOW_REMOVED_MASTER:
+                master[OAM_FLOW_REMOVED] = ntohl(msg->mask);
+                break;
+
+            case OFPACPT_ROLE_STATUS_SLAVE:
+                slave[OAM_ROLE_STATUS] = ntohl(msg->mask);
+                break;
+
+            case OFPACPT_ROLE_STATUS_MASTER:
+                master[OAM_ROLE_STATUS] = ntohl(msg->mask);
+                break;
+
+            case OFPACPT_TABLE_STATUS_SLAVE:
+                slave[OAM_TABLE_STATUS] = ntohl(msg->mask);
+                break;
+
+            case OFPACPT_TABLE_STATUS_MASTER:
+                master[OAM_TABLE_STATUS] = ntohl(msg->mask);
+                break;
+
+            case OFPACPT_REQUESTFORWARD_SLAVE:
+                slave[OAM_REQUESTFORWARD] = ntohl(msg->mask);
+                break;
+
+            case OFPACPT_REQUESTFORWARD_MASTER:
+                master[OAM_REQUESTFORWARD] = ntohl(msg->mask);
+                break;
+
+            default:
+                error = loose ? 0 : OFPERR_OFPBPC_BAD_TYPE;
+                break;
+            }
+            if (error) {
+                return error;
+            }
+        }
+    } else {
+        return OFPERR_OFPBRC_BAD_VERSION;
+    }
+    return 0;
+}
+
+/* Append all asynchronous configuration properties in GET_ASYNC_REPLY
+ * message, describing if various set of asynchronous messages are enabled
+ * or not. */
+static enum ofperr
+ofputil_get_async_reply(struct ofpbuf *buf, const uint32_t master_mask,
+                        const uint32_t slave_mask, const uint32_t type)
+{
+    int role;
+
+    for (role = 0; role < 2; role++) {
+        struct ofp14_async_config_prop_reasons *msg;
+
+        msg = ofpbuf_put_zeros(buf, sizeof *msg);
+
+        switch (type) {
+        case OAM_PACKET_IN:
+            msg->type = (role ? htons(OFPACPT_PACKET_IN_SLAVE)
+                              : htons(OFPACPT_PACKET_IN_MASTER));
+            break;
+
+        case OAM_PORT_STATUS:
+            msg->type = (role ? htons(OFPACPT_PORT_STATUS_SLAVE)
+                              : htons(OFPACPT_PORT_STATUS_MASTER));
+            break;
+
+        case OAM_FLOW_REMOVED:
+            msg->type = (role ? htons(OFPACPT_FLOW_REMOVED_SLAVE)
+                              : htons(OFPACPT_FLOW_REMOVED_MASTER));
+            break;
+
+        case OAM_ROLE_STATUS:
+            msg->type = (role ? htons(OFPACPT_ROLE_STATUS_SLAVE)
+                              : htons(OFPACPT_ROLE_STATUS_MASTER));
+            break;
+
+        case OAM_TABLE_STATUS:
+            msg->type = (role ? htons(OFPACPT_TABLE_STATUS_SLAVE)
+                              : htons(OFPACPT_TABLE_STATUS_MASTER));
+            break;
+
+        case OAM_REQUESTFORWARD:
+            msg->type = (role ? htons(OFPACPT_REQUESTFORWARD_SLAVE)
+                              : htons(OFPACPT_REQUESTFORWARD_MASTER));
+            break;
+
+        default:
+            return OFPERR_OFPBRC_BAD_TYPE;
+        }
+        msg->length = htons(sizeof *msg);
+        msg->mask = (role ? htonl(slave_mask) : htonl(master_mask));
+    }
+
+    return 0;
+}
+
+/* Returns a OpenFlow message that encodes 'asynchronous configuration' properly
+ * as a reply to get async config request. */
+struct ofpbuf *
+ofputil_encode_get_async_config(const struct ofp_header *oh,
+                                uint32_t master[OAM_N_TYPES],
+                                uint32_t slave[OAM_N_TYPES])
+{
+    struct ofpbuf *buf;
+    uint32_t type;
+
+    buf = ofpraw_alloc_reply((oh->version < OFP14_VERSION
+                              ? OFPRAW_OFPT13_GET_ASYNC_REPLY
+                              : OFPRAW_OFPT14_GET_ASYNC_REPLY), oh, 0);
+
+    if (oh->version < OFP14_VERSION) {
+        struct nx_async_config *msg;
+        msg = ofpbuf_put_zeros(buf, sizeof *msg);
+
+        msg->packet_in_mask[0] = htonl(master[OAM_PACKET_IN]);
+        msg->port_status_mask[0] = htonl(master[OAM_PORT_STATUS]);
+        msg->flow_removed_mask[0] = htonl(master[OAM_FLOW_REMOVED]);
+
+        msg->packet_in_mask[1] = htonl(slave[OAM_PACKET_IN]);
+        msg->port_status_mask[1] = htonl(slave[OAM_PORT_STATUS]);
+        msg->flow_removed_mask[1] = htonl(slave[OAM_FLOW_REMOVED]);
+    } else if (oh->version == OFP14_VERSION) {
+        for (type = 0; type < OAM_N_TYPES; type++) {
+            ofputil_get_async_reply(buf, master[type], slave[type], type);
+        }
+    }
+
+    return buf;
+}