nicira-ext: Clarify comments about 'src' and 'dst' in NXAST_REG actions.
[openvswitch] / lib / ofp-util.c
index 208eabb5c56844efbaf9921f2ae1301806a74ac9..11836ccdd94a73e91a4394865e96abc340182833 100644 (file)
@@ -72,7 +72,6 @@ ofputil_netmask_to_wcbits(ovs_be32 netmask)
  * name. */
 #define WC_INVARIANT_LIST \
     WC_INVARIANT_BIT(IN_PORT) \
-    WC_INVARIANT_BIT(DL_VLAN) \
     WC_INVARIANT_BIT(DL_SRC) \
     WC_INVARIANT_BIT(DL_DST) \
     WC_INVARIANT_BIT(DL_TYPE) \
@@ -104,11 +103,13 @@ enum {
  * wildcarded. */
 void
 ofputil_cls_rule_from_match(const struct ofp_match *match,
-                            unsigned int priority, int flow_format,
+                            unsigned int priority,
+                            enum nx_flow_format flow_format,
                             uint64_t cookie, struct cls_rule *rule)
 {
     struct flow_wildcards *wc = &rule->wc;
     unsigned int ofpfw;
+    ovs_be16 vid, pcp;
 
     /* Initialize rule->priority. */
     ofpfw = ntohl(match->wildcards);
@@ -117,9 +118,6 @@ ofputil_cls_rule_from_match(const struct ofp_match *match,
 
     /* Initialize most of rule->wc. */
     wc->wildcards = ofpfw & WC_INVARIANTS;
-    if (ofpfw & OFPFW_DL_VLAN_PCP) {
-        wc->wildcards |= FWW_DL_VLAN_PCP;
-    }
     if (ofpfw & OFPFW_NW_TOS) {
         wc->wildcards |= FWW_NW_TOS;
     }
@@ -127,7 +125,7 @@ ofputil_cls_rule_from_match(const struct ofp_match *match,
     wc->nw_src_mask = ofputil_wcbits_to_netmask(ofpfw >> OFPFW_NW_SRC_SHIFT);
     wc->nw_dst_mask = ofputil_wcbits_to_netmask(ofpfw >> OFPFW_NW_DST_SHIFT);
 
-    if (!(ofpfw & NXFW_TUN_ID)) {
+    if (flow_format == NXFF_TUN_ID_FROM_COOKIE && !(ofpfw & NXFW_TUN_ID)) {
         rule->flow.tun_id = htonl(ntohll(cookie) >> 32);
     } else {
         wc->wildcards |= FWW_TUN_ID;
@@ -141,13 +139,11 @@ ofputil_cls_rule_from_match(const struct ofp_match *match,
         wc->wildcards |= FWW_ETH_MCAST;
     }
 
-    /* Initialize rule->flow. */
+    /* Initialize most of rule->flow. */
     rule->flow.nw_src = match->nw_src;
     rule->flow.nw_dst = match->nw_dst;
     rule->flow.in_port = (match->in_port == htons(OFPP_LOCAL) ? ODPP_LOCAL
                      : ntohs(match->in_port));
-    rule->flow.dl_vlan = match->dl_vlan;
-    rule->flow.dl_vlan_pcp = match->dl_vlan_pcp;
     rule->flow.dl_type = match->dl_type;
     rule->flow.tp_src = match->tp_src;
     rule->flow.tp_dst = match->tp_dst;
@@ -156,6 +152,49 @@ ofputil_cls_rule_from_match(const struct ofp_match *match,
     rule->flow.nw_tos = match->nw_tos;
     rule->flow.nw_proto = match->nw_proto;
 
+    /* Translate VLANs. */
+    vid = match->dl_vlan & htons(VLAN_VID_MASK);
+    pcp = htons((match->dl_vlan_pcp << VLAN_PCP_SHIFT) & VLAN_PCP_MASK);
+    switch (ofpfw & (OFPFW_DL_VLAN | OFPFW_DL_VLAN_PCP)) {
+    case OFPFW_DL_VLAN | OFPFW_DL_VLAN_PCP:
+        /* Wildcard everything. */
+        rule->flow.vlan_tci = htons(0);
+        rule->wc.vlan_tci_mask = htons(0);
+        break;
+
+    case OFPFW_DL_VLAN_PCP:
+        if (match->dl_vlan == htons(OFP_VLAN_NONE)) {
+            /* Match only packets without 802.1Q header. */
+            rule->flow.vlan_tci = htons(0);
+            rule->wc.vlan_tci_mask = htons(0xffff);
+        } else {
+            /* Wildcard PCP, specific VID. */
+            rule->flow.vlan_tci = vid | htons(VLAN_CFI);
+            rule->wc.vlan_tci_mask = htons(VLAN_VID_MASK | VLAN_CFI);
+        }
+        break;
+
+    case OFPFW_DL_VLAN:
+        /* Wildcard VID, specific PCP. */
+        rule->flow.vlan_tci = pcp | htons(VLAN_CFI);
+        rule->wc.vlan_tci_mask = htons(VLAN_PCP_MASK | VLAN_CFI);
+        break;
+
+    case 0:
+        if (match->dl_vlan == htons(OFP_VLAN_NONE)) {
+            /* This case is odd, since we can't have a specific PCP without an
+             * 802.1Q header.  However, older versions of OVS treated this as
+             * matching packets withut an 802.1Q header, so we do here too. */
+            rule->flow.vlan_tci = htons(0);
+            rule->wc.vlan_tci_mask = htons(0xffff);
+        } else {
+            /* Specific VID and PCP. */
+            rule->flow.vlan_tci = vid | pcp | htons(VLAN_CFI);
+            rule->wc.vlan_tci_mask = htons(0xffff);
+        }
+        break;
+    }
+
     /* Clean up. */
     cls_rule_zero_wildcarded_fields(rule);
 }
@@ -167,19 +206,17 @@ ofputil_cls_rule_from_match(const struct ofp_match *match,
  * the latter case only, 'match''s NXFW_TUN_ID bit will be filled in; otherwise
  * it is always set to 0. */
 void
-ofputil_cls_rule_to_match(const struct cls_rule *rule, int flow_format,
+ofputil_cls_rule_to_match(const struct cls_rule *rule,
+                          enum nx_flow_format flow_format,
                           struct ofp_match *match)
 {
     const struct flow_wildcards *wc = &rule->wc;
     unsigned int ofpfw;
 
-    /* Figure out OpenFlow wildcards. */
+    /* Figure out most OpenFlow wildcards. */
     ofpfw = wc->wildcards & WC_INVARIANTS;
     ofpfw |= ofputil_netmask_to_wcbits(wc->nw_src_mask) << OFPFW_NW_SRC_SHIFT;
     ofpfw |= ofputil_netmask_to_wcbits(wc->nw_dst_mask) << OFPFW_NW_DST_SHIFT;
-    if (wc->wildcards & FWW_DL_VLAN_PCP) {
-        ofpfw |= OFPFW_DL_VLAN_PCP;
-    }
     if (wc->wildcards & FWW_NW_TOS) {
         ofpfw |= OFPFW_NW_TOS;
     }
@@ -187,12 +224,32 @@ ofputil_cls_rule_to_match(const struct cls_rule *rule, int flow_format,
         ofpfw |= NXFW_TUN_ID;
     }
 
-    /* Compose match structure. */
+    /* Translate VLANs. */
+    match->dl_vlan = htons(0);
+    match->dl_vlan_pcp = 0;
+    if (rule->wc.vlan_tci_mask == htons(0)) {
+        ofpfw |= OFPFW_DL_VLAN | OFPFW_DL_VLAN_PCP;
+    } else if (rule->wc.vlan_tci_mask & htons(VLAN_CFI)
+               && !(rule->flow.vlan_tci & htons(VLAN_CFI))) {
+        match->dl_vlan = htons(OFP_VLAN_NONE);
+    } else {
+        if (!(rule->wc.vlan_tci_mask & htons(VLAN_VID_MASK))) {
+            ofpfw |= OFPFW_DL_VLAN;
+        } else {
+            match->dl_vlan = htons(vlan_tci_to_vid(rule->flow.vlan_tci));
+        }
+
+        if (!(rule->wc.vlan_tci_mask & htons(VLAN_PCP_MASK))) {
+            ofpfw |= OFPFW_DL_VLAN_PCP;
+        } else {
+            match->dl_vlan_pcp = vlan_tci_to_pcp(rule->flow.vlan_tci);
+        }
+    }
+
+    /* Compose most of the match structure. */
     match->wildcards = htonl(ofpfw);
     match->in_port = htons(rule->flow.in_port == ODPP_LOCAL ? OFPP_LOCAL
                            : rule->flow.in_port);
-    match->dl_vlan = rule->flow.dl_vlan;
-    match->dl_vlan_pcp = rule->flow.dl_vlan_pcp;
     memcpy(match->dl_src, rule->flow.dl_src, ETH_ADDR_LEN);
     memcpy(match->dl_dst, rule->flow.dl_dst, ETH_ADDR_LEN);
     match->dl_type = rule->flow.dl_type;
@@ -213,7 +270,502 @@ alloc_xid(void)
     static uint32_t next_xid = 1;
     return htonl(next_xid++);
 }
+\f
+/* Basic parsing of OpenFlow messages. */
+
+struct ofputil_msg_type {
+    enum ofputil_msg_code code; /* OFPUTIL_*. */
+    uint32_t value;             /* OFPT_*, OFPST_*, NXT_*, or NXST_*. */
+    const char *name;           /* e.g. "OFPT_FLOW_REMOVED". */
+    unsigned int min_size;      /* Minimum total message size in bytes. */
+    /* 0 if 'min_size' is the exact size that the message must be.  Otherwise,
+     * the message may exceed 'min_size' by an even multiple of this value. */
+    unsigned int extra_multiple;
+};
+
+struct ofputil_msg_category {
+    const char *name;           /* e.g. "OpenFlow message" */
+    const struct ofputil_msg_type *types;
+    size_t n_types;
+    int missing_error;          /* ofp_mkerr() value for missing type. */
+};
+
+static bool
+ofputil_length_ok(const struct ofputil_msg_category *cat,
+                  const struct ofputil_msg_type *type,
+                  unsigned int size)
+{
+    switch (type->extra_multiple) {
+    case 0:
+        if (size != type->min_size) {
+            VLOG_WARN_RL(&bad_ofmsg_rl, "received %s %s with incorrect "
+                         "length %u (expected length %u)",
+                         cat->name, type->name, size, type->min_size);
+            return false;
+        }
+        return true;
+
+    case 1:
+        if (size < type->min_size) {
+            VLOG_WARN_RL(&bad_ofmsg_rl, "received %s %s with incorrect "
+                         "length %u (expected length at least %u bytes)",
+                         cat->name, type->name, size, type->min_size);
+            return false;
+        }
+        return true;
+
+    default:
+        if (size < type->min_size
+            || (size - type->min_size) % type->extra_multiple) {
+            VLOG_WARN_RL(&bad_ofmsg_rl, "received %s %s with incorrect "
+                         "length %u (must be exactly %u bytes or longer "
+                         "by an integer multiple of %u bytes)",
+                         cat->name, type->name, size,
+                         type->min_size, type->extra_multiple);
+            return false;
+        }
+        return true;
+    }
+}
+
+static int
+ofputil_lookup_openflow_message(const struct ofputil_msg_category *cat,
+                                uint32_t value, unsigned int size,
+                                const struct ofputil_msg_type **typep)
+{
+    const struct ofputil_msg_type *type;
+
+    for (type = cat->types; type < &cat->types[cat->n_types]; type++) {
+        if (type->value == value) {
+            if (!ofputil_length_ok(cat, type, size)) {
+                return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_LEN);
+            }
+            *typep = type;
+            return 0;
+        }
+    }
+
+    VLOG_WARN_RL(&bad_ofmsg_rl, "received %s of unknown type %u",
+                 cat->name, value);
+    return cat->missing_error;
+}
+
+static int
+ofputil_decode_vendor(const struct ofp_header *oh,
+                      const struct ofputil_msg_type **typep)
+{
+    static const struct ofputil_msg_type nxt_messages[] = {
+        { OFPUTIL_NXT_STATUS_REQUEST,
+          NXT_STATUS_REQUEST, "NXT_STATUS_REQUEST",
+          sizeof(struct nicira_header), 1 },
+
+        { OFPUTIL_NXT_STATUS_REPLY,
+          NXT_STATUS_REPLY, "NXT_STATUS_REPLY",
+          sizeof(struct nicira_header), 1 },
+
+        { OFPUTIL_NXT_TUN_ID_FROM_COOKIE,
+          NXT_TUN_ID_FROM_COOKIE, "NXT_TUN_ID_FROM_COOKIE",
+          sizeof(struct nxt_tun_id_cookie), 0 },
+
+        { OFPUTIL_NXT_ROLE_REQUEST,
+          NXT_ROLE_REQUEST, "NXT_ROLE_REQUEST",
+          sizeof(struct nx_role_request), 0 },
+
+        { OFPUTIL_NXT_ROLE_REPLY,
+          NXT_ROLE_REPLY, "NXT_ROLE_REPLY",
+          sizeof(struct nx_role_request), 0 },
+
+        { OFPUTIL_NXT_SET_FLOW_FORMAT,
+          NXT_SET_FLOW_FORMAT, "NXT_SET_FLOW_FORMAT",
+          sizeof(struct nxt_set_flow_format), 0 },
+
+        { OFPUTIL_NXT_FLOW_MOD,
+          NXT_FLOW_MOD, "NXT_FLOW_MOD",
+          sizeof(struct nx_flow_mod), 8 },
+
+        { OFPUTIL_NXT_FLOW_REMOVED,
+          NXT_FLOW_REMOVED, "NXT_FLOW_REMOVED",
+          sizeof(struct nx_flow_removed), 8 },
+    };
+
+    static const struct ofputil_msg_category nxt_category = {
+        "Nicira extension message",
+        nxt_messages, ARRAY_SIZE(nxt_messages),
+        OFP_MKERR(OFPET_BAD_REQUEST, OFPBRC_BAD_SUBTYPE)
+    };
+
+    const struct ofp_vendor_header *ovh;
+    const struct nicira_header *nh;
+
+    ovh = (const struct ofp_vendor_header *) oh;
+    if (ovh->vendor != htonl(NX_VENDOR_ID)) {
+        VLOG_WARN_RL(&bad_ofmsg_rl, "received vendor message for unknown "
+                     "vendor %"PRIx32, ntohl(ovh->vendor));
+        return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_VENDOR);
+    }
+
+    if (ntohs(ovh->header.length) < sizeof(struct nicira_header)) {
+        VLOG_WARN_RL(&bad_ofmsg_rl, "received Nicira vendor message of "
+                     "length %u (expected at least %zu)",
+                     ntohs(ovh->header.length), sizeof(struct nicira_header));
+        return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_LEN);
+    }
+
+    nh = (const struct nicira_header *) oh;
+    return ofputil_lookup_openflow_message(&nxt_category, ntohl(nh->subtype),
+                                           ntohs(oh->length), typep);
+}
+
+static int
+check_nxstats_msg(const struct ofp_header *oh)
+{
+    const struct ofp_stats_request *osr;
+    ovs_be32 vendor;
+
+    osr = (const struct ofp_stats_request *) oh;
+
+    memcpy(&vendor, osr->body, sizeof vendor);
+    if (vendor != htonl(NX_VENDOR_ID)) {
+        VLOG_WARN_RL(&bad_ofmsg_rl, "received vendor stats message for "
+                     "unknown vendor %"PRIx32, ntohl(vendor));
+        return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_VENDOR);
+    }
+
+    if (ntohs(osr->header.length) < sizeof(struct nicira_stats_msg)) {
+        VLOG_WARN_RL(&bad_ofmsg_rl, "truncated Nicira stats message");
+        return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_LEN);
+    }
+
+    return 0;
+}
+
+static int
+ofputil_decode_nxst_request(const struct ofp_header *oh,
+                            const struct ofputil_msg_type **typep)
+{
+    static const struct ofputil_msg_type nxst_requests[] = {
+        { OFPUTIL_NXST_FLOW_REQUEST,
+          NXST_FLOW, "NXST_FLOW request",
+          sizeof(struct nx_flow_stats_request), 8 },
+
+        { OFPUTIL_NXST_AGGREGATE_REQUEST,
+          NXST_AGGREGATE, "NXST_AGGREGATE request",
+          sizeof(struct nx_aggregate_stats_request), 8 },
+    };
+
+    static const struct ofputil_msg_category nxst_request_category = {
+        "Nicira extension statistics",
+        nxst_requests, ARRAY_SIZE(nxst_requests),
+        OFP_MKERR(OFPET_BAD_REQUEST, OFPBRC_BAD_SUBTYPE)
+    };
+
+    const struct nicira_stats_msg *nsm;
+    int error;
+
+    error = check_nxstats_msg(oh);
+    if (error) {
+        return error;
+    }
+
+    nsm = (struct nicira_stats_msg *) oh;
+    return ofputil_lookup_openflow_message(&nxst_request_category,
+                                           ntohl(nsm->subtype),
+                                           ntohs(oh->length), typep);
+}
+
+static int
+ofputil_decode_nxst_reply(const struct ofp_header *oh,
+                          const struct ofputil_msg_type **typep)
+{
+    static const struct ofputil_msg_type nxst_replies[] = {
+        { OFPUTIL_NXST_FLOW_REPLY,
+          NXST_FLOW, "NXST_FLOW reply",
+          sizeof(struct nicira_stats_msg), 8 },
+
+        { OFPUTIL_NXST_AGGREGATE_REPLY,
+          NXST_AGGREGATE, "NXST_AGGREGATE reply",
+          sizeof(struct nx_aggregate_stats_reply), 0 },
+    };
+
+    static const struct ofputil_msg_category nxst_reply_category = {
+        "Nicira extension statistics",
+        nxst_replies, ARRAY_SIZE(nxst_replies),
+        OFP_MKERR(OFPET_BAD_REQUEST, OFPBRC_BAD_SUBTYPE)
+    };
+
+    const struct nicira_stats_msg *nsm;
+    int error;
+
+    error = check_nxstats_msg(oh);
+    if (error) {
+        return error;
+    }
+
+    nsm = (struct nicira_stats_msg *) oh;
+    return ofputil_lookup_openflow_message(&nxst_reply_category,
+                                           ntohl(nsm->subtype),
+                                           ntohs(oh->length), typep);
+}
+
+static int
+ofputil_decode_ofpst_request(const struct ofp_header *oh,
+                             const struct ofputil_msg_type **typep)
+{
+    enum { OSR_SIZE = sizeof(struct ofp_stats_request) };
+    static const struct ofputil_msg_type ofpst_requests[] = {
+        { OFPUTIL_OFPST_DESC_REQUEST,
+          OFPST_DESC, "OFPST_DESC request",
+          OSR_SIZE, 0 },
+
+        { OFPUTIL_OFPST_FLOW_REQUEST,
+          OFPST_FLOW, "OFPST_FLOW request",
+          OSR_SIZE + sizeof(struct ofp_flow_stats_request), 0 },
+
+        { OFPUTIL_OFPST_AGGREGATE_REQUEST,
+          OFPST_AGGREGATE, "OFPST_AGGREGATE request",
+          OSR_SIZE + sizeof(struct ofp_aggregate_stats_request), 0 },
+
+        { OFPUTIL_OFPST_TABLE_REQUEST,
+          OFPST_TABLE, "OFPST_TABLE request",
+          OSR_SIZE, 0 },
+
+        { OFPUTIL_OFPST_PORT_REQUEST,
+          OFPST_PORT, "OFPST_PORT request",
+          OSR_SIZE + sizeof(struct ofp_port_stats_request), 0 },
+
+        { OFPUTIL_OFPST_QUEUE_REQUEST,
+          OFPST_QUEUE, "OFPST_QUEUE request",
+          OSR_SIZE + sizeof(struct ofp_queue_stats_request), 0 },
+
+        { 0,
+          OFPST_VENDOR, "OFPST_VENDOR request",
+          OSR_SIZE + sizeof(uint32_t), 1 },
+    };
+
+    static const struct ofputil_msg_category ofpst_request_category = {
+        "OpenFlow statistics",
+        ofpst_requests, ARRAY_SIZE(ofpst_requests),
+        OFP_MKERR(OFPET_BAD_REQUEST, OFPBRC_BAD_STAT)
+    };
+
+    const struct ofp_stats_request *osr;
+    int error;
+
+    osr = (const struct ofp_stats_request *) oh;
+    error = ofputil_lookup_openflow_message(&ofpst_request_category,
+                                            ntohs(osr->type),
+                                            ntohs(oh->length), typep);
+    if (!error && osr->type == htons(OFPST_VENDOR)) {
+        error = ofputil_decode_nxst_request(oh, typep);
+    }
+    return error;
+}
+
+static int
+ofputil_decode_ofpst_reply(const struct ofp_header *oh,
+                           const struct ofputil_msg_type **typep)
+{
+    enum { OSR_SIZE = sizeof(struct ofp_stats_reply) };
+    static const struct ofputil_msg_type ofpst_replies[] = {
+        { OFPUTIL_OFPST_DESC_REPLY,
+          OFPST_DESC, "OFPST_DESC reply",
+          OSR_SIZE + sizeof(struct ofp_desc_stats), 0 },
+
+        { OFPUTIL_OFPST_FLOW_REPLY,
+          OFPST_FLOW, "OFPST_FLOW reply",
+          OSR_SIZE, 1 },
+
+        { OFPUTIL_OFPST_AGGREGATE_REPLY,
+          OFPST_AGGREGATE, "OFPST_AGGREGATE reply",
+          OSR_SIZE + sizeof(struct ofp_aggregate_stats_reply), 0 },
+
+        { OFPUTIL_OFPST_TABLE_REPLY,
+          OFPST_TABLE, "OFPST_TABLE reply",
+          OSR_SIZE, sizeof(struct ofp_table_stats) },
+
+        { OFPUTIL_OFPST_PORT_REPLY,
+          OFPST_PORT, "OFPST_PORT reply",
+          OSR_SIZE, sizeof(struct ofp_port_stats) },
+
+        { OFPUTIL_OFPST_QUEUE_REPLY,
+          OFPST_QUEUE, "OFPST_QUEUE reply",
+          OSR_SIZE, sizeof(struct ofp_queue_stats) },
+
+        { 0,
+          OFPST_VENDOR, "OFPST_VENDOR reply",
+          OSR_SIZE + sizeof(uint32_t), 1 },
+    };
+
+    static const struct ofputil_msg_category ofpst_reply_category = {
+        "OpenFlow statistics",
+        ofpst_replies, ARRAY_SIZE(ofpst_replies),
+        OFP_MKERR(OFPET_BAD_REQUEST, OFPBRC_BAD_STAT)
+    };
+
+    const struct ofp_stats_reply *osr = (const struct ofp_stats_reply *) oh;
+    int error;
+
+    error = ofputil_lookup_openflow_message(&ofpst_reply_category,
+                                           ntohs(osr->type),
+                                           ntohs(oh->length), typep);
+    if (!error && osr->type == htons(OFPST_VENDOR)) {
+        error = ofputil_decode_nxst_reply(oh, typep);
+    }
+    return error;
+}
+
+/* Decodes the message type represented by 'oh'.  Returns 0 if successful or
+ * an OpenFlow error code constructed with ofp_mkerr() on failure.  Either
+ * way, stores in '*typep' a type structure that can be inspected with the
+ * ofputil_msg_type_*() functions.
+ *
+ * oh->length must indicate the correct length of the message (and must be at
+ * least sizeof(struct ofp_header)).
+ *
+ * Success indicates that 'oh' is at least as long as the minimum-length
+ * message of its type. */
+int
+ofputil_decode_msg_type(const struct ofp_header *oh,
+                        const struct ofputil_msg_type **typep)
+{
+    static const struct ofputil_msg_type ofpt_messages[] = {
+        { OFPUTIL_OFPT_HELLO,
+          OFPT_HELLO, "OFPT_HELLO",
+          sizeof(struct ofp_hello), 1 },
+
+        { OFPUTIL_OFPT_ERROR,
+          OFPT_ERROR, "OFPT_ERROR",
+          sizeof(struct ofp_error_msg), 1 },
+
+        { OFPUTIL_OFPT_ECHO_REQUEST,
+          OFPT_ECHO_REQUEST, "OFPT_ECHO_REQUEST",
+          sizeof(struct ofp_header), 1 },
+
+        { OFPUTIL_OFPT_ECHO_REPLY,
+          OFPT_ECHO_REPLY, "OFPT_ECHO_REPLY",
+          sizeof(struct ofp_header), 1 },
+
+        { OFPUTIL_OFPT_FEATURES_REQUEST,
+          OFPT_FEATURES_REQUEST, "OFPT_FEATURES_REQUEST",
+          sizeof(struct ofp_header), 0 },
+
+        { OFPUTIL_OFPT_FEATURES_REPLY,
+          OFPT_FEATURES_REPLY, "OFPT_FEATURES_REPLY",
+          sizeof(struct ofp_switch_features), sizeof(struct ofp_phy_port) },
+
+        { OFPUTIL_OFPT_GET_CONFIG_REQUEST,
+          OFPT_GET_CONFIG_REQUEST, "OFPT_GET_CONFIG_REQUEST",
+          sizeof(struct ofp_header), 0 },
+
+        { OFPUTIL_OFPT_GET_CONFIG_REPLY,
+          OFPT_GET_CONFIG_REPLY, "OFPT_GET_CONFIG_REPLY",
+          sizeof(struct ofp_switch_config), 0 },
+
+        { OFPUTIL_OFPT_SET_CONFIG,
+          OFPT_SET_CONFIG, "OFPT_SET_CONFIG",
+          sizeof(struct ofp_switch_config), 0 },
+
+        { OFPUTIL_OFPT_PACKET_IN,
+          OFPT_PACKET_IN, "OFPT_PACKET_IN",
+          offsetof(struct ofp_packet_in, data), 1 },
+
+        { OFPUTIL_OFPT_FLOW_REMOVED,
+          OFPT_FLOW_REMOVED, "OFPT_FLOW_REMOVED",
+          sizeof(struct ofp_flow_removed), 0 },
+
+        { OFPUTIL_OFPT_PORT_STATUS,
+          OFPT_PORT_STATUS, "OFPT_PORT_STATUS",
+          sizeof(struct ofp_port_status), 0 },
+
+        { OFPUTIL_OFPT_PACKET_OUT,
+          OFPT_PACKET_OUT, "OFPT_PACKET_OUT",
+          sizeof(struct ofp_packet_out), 1 },
+
+        { OFPUTIL_OFPT_FLOW_MOD,
+          OFPT_FLOW_MOD, "OFPT_FLOW_MOD",
+          sizeof(struct ofp_flow_mod), 1 },
+
+        { OFPUTIL_OFPT_PORT_MOD,
+          OFPT_PORT_MOD, "OFPT_PORT_MOD",
+          sizeof(struct ofp_port_mod), 0 },
+
+        { 0,
+          OFPT_STATS_REQUEST, "OFPT_STATS_REQUEST",
+          sizeof(struct ofp_stats_request), 1 },
+
+        { 0,
+          OFPT_STATS_REPLY, "OFPT_STATS_REPLY",
+          sizeof(struct ofp_stats_reply), 1 },
+
+        { OFPUTIL_OFPT_BARRIER_REQUEST,
+          OFPT_BARRIER_REQUEST, "OFPT_BARRIER_REQUEST",
+          sizeof(struct ofp_header), 0 },
+
+        { OFPUTIL_OFPT_BARRIER_REPLY,
+          OFPT_BARRIER_REPLY, "OFPT_BARRIER_REPLY",
+          sizeof(struct ofp_header), 0 },
+
+        { 0,
+          OFPT_VENDOR, "OFPT_VENDOR",
+          sizeof(struct ofp_vendor_header), 1 },
+    };
+
+    static const struct ofputil_msg_category ofpt_category = {
+        "OpenFlow message",
+        ofpt_messages, ARRAY_SIZE(ofpt_messages),
+        OFP_MKERR(OFPET_BAD_REQUEST, OFPBRC_BAD_TYPE)
+    };
 
+    int error;
+
+    error = ofputil_lookup_openflow_message(&ofpt_category, oh->type,
+                                            ntohs(oh->length), typep);
+    if (!error) {
+        switch (oh->type) {
+        case OFPT_VENDOR:
+            error = ofputil_decode_vendor(oh, typep);
+            break;
+
+        case OFPT_STATS_REQUEST:
+            error = ofputil_decode_ofpst_request(oh, typep);
+            break;
+
+        case OFPT_STATS_REPLY:
+            error = ofputil_decode_ofpst_reply(oh, typep);
+
+        default:
+            break;
+        }
+    }
+    if (error) {
+        static const struct ofputil_msg_type ofputil_invalid_type = {
+            OFPUTIL_INVALID,
+            0, "OFPUTIL_INVALID",
+            0, 0
+        };
+
+        *typep = &ofputil_invalid_type;
+    }
+    return error;
+}
+
+/* Returns an OFPUTIL_* message type code for 'type'. */
+enum ofputil_msg_code
+ofputil_msg_type_code(const struct ofputil_msg_type *type)
+{
+    return type->code;
+}
+
+/* Returns a string representing the message type of 'type'.  The string is the
+ * enumeration constant for the type, e.g. "OFPT_HELLO".  For statistics
+ * messages, the constant is followed by "request" or "reply",
+ * e.g. "OFPST_AGGREGATE reply". */
+const char *
+ofputil_msg_type_name(const struct ofputil_msg_type *type)
+{
+    return type->name;
+}
+\f
 /* Allocates and stores in '*bufferp' a new ofpbuf with a size of
  * 'openflow_len', starting with an OpenFlow header with the given 'type' and
  * an arbitrary transaction id.  Allocated bytes beyond the header, if any, are
@@ -269,11 +821,8 @@ void *
 make_nxmsg_xid(size_t openflow_len, uint32_t subtype, ovs_be32 xid,
                struct ofpbuf **bufferp)
 {
-    struct nicira_header *nxh = make_openflow_xid(openflow_len, OFPT_VENDOR,
-                                                  xid, bufferp);
-    nxh->vendor = htonl(NX_VENDOR_ID);
-    nxh->subtype = htonl(subtype);
-    return nxh;
+    *bufferp = ofpbuf_new(openflow_len);
+    return put_nxmsg_xid(openflow_len, subtype, xid, *bufferp);
 }
 
 /* Appends 'openflow_len' bytes to 'buffer', starting with an OpenFlow header
@@ -318,6 +867,28 @@ put_openflow_xid(size_t openflow_len, uint8_t type, ovs_be32 xid,
     return oh;
 }
 
+/* Similar to put_openflow() but append a Nicira vendor extension message with
+ * the specific 'subtype'.  'subtype' should be in host byte order. */
+void *
+put_nxmsg(size_t openflow_len, uint32_t subtype, struct ofpbuf *buffer)
+{
+    return put_nxmsg_xid(openflow_len, subtype, alloc_xid(), buffer);
+}
+
+/* Similar to put_openflow_xid() but append a Nicira vendor extension message
+ * with the specific 'subtype'.  'subtype' should be in host byte order. */
+void *
+put_nxmsg_xid(size_t openflow_len, uint32_t subtype, ovs_be32 xid,
+              struct ofpbuf *buffer)
+{
+    struct nicira_header *nxh;
+
+    nxh = put_openflow_xid(openflow_len, OFPT_VENDOR, xid, buffer);
+    nxh->vendor = htonl(NX_VENDOR_ID);
+    nxh->subtype = htonl(subtype);
+    return nxh;
+}
+
 /* Updates the 'length' field of the OpenFlow message in 'buffer' to
  * 'buffer->size'. */
 void
@@ -327,6 +898,55 @@ update_openflow_length(struct ofpbuf *buffer)
     oh->length = htons(buffer->size);
 }
 
+/* Creates an ofp_stats_request with the given 'type' and 'body_len' bytes of
+ * space allocated for the 'body' member.  Returns the first byte of the 'body'
+ * member. */
+void *
+ofputil_make_stats_request(size_t body_len, uint16_t type,
+                           struct ofpbuf **bufferp)
+{
+    struct ofp_stats_request *osr;
+    osr = make_openflow((offsetof(struct ofp_stats_request, body)
+                        + body_len), OFPT_STATS_REQUEST, bufferp);
+    osr->type = htons(type);
+    osr->flags = htons(0);
+    return osr->body;
+}
+
+/* Creates a stats request message with Nicira as vendor and the given
+ * 'subtype', of total length 'openflow_len'.  Returns the message. */
+void *
+ofputil_make_nxstats_request(size_t openflow_len, uint32_t subtype,
+                             struct ofpbuf **bufferp)
+{
+    struct nicira_stats_msg *nsm;
+
+    nsm = make_openflow(openflow_len, OFPT_STATS_REQUEST, bufferp);
+    nsm->type = htons(OFPST_VENDOR);
+    nsm->flags = htons(0);
+    nsm->vendor = htonl(NX_VENDOR_ID);
+    nsm->subtype = htonl(subtype);
+    return nsm;
+}
+
+/* Returns the first byte of the 'body' member of the ofp_stats_request or
+ * ofp_stats_reply in 'oh'. */
+const void *
+ofputil_stats_body(const struct ofp_header *oh)
+{
+    assert(oh->type == OFPT_STATS_REQUEST || oh->type == OFPT_STATS_REPLY);
+    return ((const struct ofp_stats_request *) oh)->body;
+}
+
+/* Returns the length of the 'body' member of the ofp_stats_request or
+ * ofp_stats_reply in 'oh'. */
+size_t
+ofputil_stats_body_len(const struct ofp_header *oh)
+{
+    assert(oh->type == OFPT_STATS_REQUEST || oh->type == OFPT_STATS_REPLY);
+    return ntohs(oh->length) - sizeof(struct ofp_stats_request);
+}
+
 struct ofpbuf *
 make_flow_mod(uint16_t command, const struct cls_rule *rule,
               size_t actions_len)
@@ -487,98 +1107,6 @@ make_echo_reply(const struct ofp_header *rq)
     return out;
 }
 
-static int
-check_message_type(uint8_t got_type, uint8_t want_type)
-{
-    if (got_type != want_type) {
-        char *want_type_name = ofp_message_type_to_string(want_type);
-        char *got_type_name = ofp_message_type_to_string(got_type);
-        VLOG_WARN_RL(&bad_ofmsg_rl,
-                     "received bad message type %s (expected %s)",
-                     got_type_name, want_type_name);
-        free(want_type_name);
-        free(got_type_name);
-        return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_TYPE);
-    }
-    return 0;
-}
-
-/* Checks that 'msg' has type 'type' and that it is exactly 'size' bytes long.
- * Returns 0 if the checks pass, otherwise an OpenFlow error code (produced
- * with ofp_mkerr()). */
-int
-check_ofp_message(const struct ofp_header *msg, uint8_t type, size_t size)
-{
-    size_t got_size;
-    int error;
-
-    error = check_message_type(msg->type, type);
-    if (error) {
-        return error;
-    }
-
-    got_size = ntohs(msg->length);
-    if (got_size != size) {
-        char *type_name = ofp_message_type_to_string(type);
-        VLOG_WARN_RL(&bad_ofmsg_rl,
-                     "received %s message of length %zu (expected %zu)",
-                     type_name, got_size, size);
-        free(type_name);
-        return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_LEN);
-    }
-
-    return 0;
-}
-
-/* Checks that 'msg' has type 'type' and that 'msg' is 'size' plus a
- * nonnegative integer multiple of 'array_elt_size' bytes long.  Returns 0 if
- * the checks pass, otherwise an OpenFlow error code (produced with
- * ofp_mkerr()).
- *
- * If 'n_array_elts' is nonnull, then '*n_array_elts' is set to the number of
- * 'array_elt_size' blocks in 'msg' past the first 'min_size' bytes, when
- * successful. */
-int
-check_ofp_message_array(const struct ofp_header *msg, uint8_t type,
-                        size_t min_size, size_t array_elt_size,
-                        size_t *n_array_elts)
-{
-    size_t got_size;
-    int error;
-
-    assert(array_elt_size);
-
-    error = check_message_type(msg->type, type);
-    if (error) {
-        return error;
-    }
-
-    got_size = ntohs(msg->length);
-    if (got_size < min_size) {
-        char *type_name = ofp_message_type_to_string(type);
-        VLOG_WARN_RL(&bad_ofmsg_rl, "received %s message of length %zu "
-                     "(expected at least %zu)",
-                     type_name, got_size, min_size);
-        free(type_name);
-        return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_LEN);
-    }
-    if ((got_size - min_size) % array_elt_size) {
-        char *type_name = ofp_message_type_to_string(type);
-        VLOG_WARN_RL(&bad_ofmsg_rl,
-                     "received %s message of bad length %zu: the "
-                     "excess over %zu (%zu) is not evenly divisible by %zu "
-                     "(remainder is %zu)",
-                     type_name, got_size, min_size, got_size - min_size,
-                     array_elt_size, (got_size - min_size) % array_elt_size);
-        free(type_name);
-        return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_LEN);
-    }
-    if (n_array_elts) {
-        *n_array_elts = (got_size - min_size) / array_elt_size;
-    }
-    return 0;
-}
-
 const struct ofp_flow_stats *
 flow_stats_first(struct flow_stats_iterator *iter,
                  const struct ofp_stats_reply *osr)