[ovs-dev] [tests+nxm-ofctl 34/42] Refactor and centralize basic OpenFlow message decoding and validation.

Ben Pfaff blp at nicira.com
Tue Nov 23 22:44:06 UTC 2010


Open vSwitch contains a few different chunks of code that need to decode
an OpenFlow message to determine its type and then validate that it is
long enough.  Until now, the code for doing this has been more or less
scattered across the tree.  Whenever a new piece of code needed to do this,
it generally needed to reimplement at least part of it.

This commit centralizes all of that work into a single function,
ofputil_decode_msg_type(), and helper functions, and converts all of the
code that was decoding messages by hand to use the new function.
---
 lib/learning-switch.c |  148 ++++++-----
 lib/ofp-print.c       |  718 +++++++++++++++++++++---------------------------
 lib/ofp-util.c        |  592 ++++++++++++++++++++++++++++++++++-------
 lib/ofp-util.h        |   74 +++++-
 ofproto/ofproto.c     |  432 ++++++++++++------------------
 ofproto/status.c      |    3 +-
 ofproto/status.h      |    8 +-
 tests/ofproto.at      |    8 +-
 tests/ovs-ofctl.at    |   18 +-
 9 files changed, 1151 insertions(+), 850 deletions(-)

diff --git a/lib/learning-switch.c b/lib/learning-switch.c
index 7b99a93..cbd24cb 100644
--- a/lib/learning-switch.c
+++ b/lib/learning-switch.c
@@ -76,10 +76,12 @@ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(30, 300);
 static void queue_tx(struct lswitch *, struct rconn *, struct ofpbuf *);
 static void send_features_request(struct lswitch *, struct rconn *);
 
-typedef void packet_handler_func(struct lswitch *, struct rconn *, void *);
-static packet_handler_func process_switch_features;
-static packet_handler_func process_packet_in;
-static packet_handler_func process_echo_request;
+static void process_switch_features(struct lswitch *,
+                                    struct ofp_switch_features *);
+static void process_packet_in(struct lswitch *, struct rconn *,
+                              const struct ofp_packet_in *);
+static void process_echo_request(struct lswitch *, struct rconn *,
+                                 const struct ofp_header *);
 
 /* Creates and returns a new learning switch whose configuration is given by
  * 'cfg'.
@@ -180,38 +182,9 @@ void
 lswitch_process_packet(struct lswitch *sw, struct rconn *rconn,
                        const struct ofpbuf *msg)
 {
-    struct processor {
-        uint8_t type;
-        size_t min_size;
-        packet_handler_func *handler;
-    };
-    static const struct processor processors[] = {
-        {
-            OFPT_ECHO_REQUEST,
-            sizeof(struct ofp_header),
-            process_echo_request
-        },
-        {
-            OFPT_FEATURES_REPLY,
-            sizeof(struct ofp_switch_features),
-            process_switch_features
-        },
-        {
-            OFPT_PACKET_IN,
-            offsetof(struct ofp_packet_in, data),
-            process_packet_in
-        },
-        {
-            OFPT_FLOW_REMOVED,
-            sizeof(struct ofp_flow_removed),
-            NULL
-        },
-    };
-    const size_t n_processors = ARRAY_SIZE(processors);
-    const struct processor *p;
-    struct ofp_header *oh;
-
-    oh = msg->data;
+    const struct ofp_header *oh = msg->data;
+    const struct ofputil_msg_type *type;
+
     if (sw->datapath_id == 0
         && oh->type != OFPT_ECHO_REQUEST
         && oh->type != OFPT_FEATURES_REPLY) {
@@ -219,27 +192,72 @@ lswitch_process_packet(struct lswitch *sw, struct rconn *rconn,
         return;
     }
 
-    for (p = processors; p < &processors[n_processors]; p++) {
-        if (oh->type == p->type) {
-            if (msg->size < p->min_size) {
-                VLOG_WARN_RL(&rl, "%016llx: %s: too short (%zu bytes) for "
-                             "type %"PRIu8" (min %zu)", sw->datapath_id,
-                             rconn_get_name(rconn), msg->size, oh->type,
-                             p->min_size);
-                return;
-            }
-            if (p->handler) {
-                (p->handler)(sw, rconn, msg->data);
-            }
-            return;
+    ofputil_decode_msg_type(oh, &type);
+    switch (ofputil_msg_type_code(type)) {
+    case OFPUTIL_OFPT_ECHO_REQUEST:
+        process_echo_request(sw, rconn, msg->data);
+        break;
+
+    case OFPUTIL_OFPT_FEATURES_REPLY:
+        process_switch_features(sw, msg->data);
+        break;
+
+    case OFPUTIL_OFPT_PACKET_IN:
+        process_packet_in(sw, rconn, msg->data);
+        break;
+
+    case OFPUTIL_OFPT_FLOW_REMOVED:
+        /* Nothing to do. */
+        break;
+
+    case OFPUTIL_INVALID:
+    case OFPUTIL_OFPT_HELLO:
+    case OFPUTIL_OFPT_ERROR:
+    case OFPUTIL_OFPT_ECHO_REPLY:
+    case OFPUTIL_OFPT_FEATURES_REQUEST:
+    case OFPUTIL_OFPT_GET_CONFIG_REQUEST:
+    case OFPUTIL_OFPT_GET_CONFIG_REPLY:
+    case OFPUTIL_OFPT_SET_CONFIG:
+    case OFPUTIL_OFPT_PORT_STATUS:
+    case OFPUTIL_OFPT_PACKET_OUT:
+    case OFPUTIL_OFPT_FLOW_MOD:
+    case OFPUTIL_OFPT_PORT_MOD:
+    case OFPUTIL_OFPT_BARRIER_REQUEST:
+    case OFPUTIL_OFPT_BARRIER_REPLY:
+    case OFPUTIL_OFPT_QUEUE_GET_CONFIG_REQUEST:
+    case OFPUTIL_OFPT_QUEUE_GET_CONFIG_REPLY:
+    case OFPUTIL_OFPST_DESC_REQUEST:
+    case OFPUTIL_OFPST_FLOW_REQUEST:
+    case OFPUTIL_OFPST_AGGREGATE_REQUEST:
+    case OFPUTIL_OFPST_TABLE_REQUEST:
+    case OFPUTIL_OFPST_PORT_REQUEST:
+    case OFPUTIL_OFPST_QUEUE_REQUEST:
+    case OFPUTIL_OFPST_DESC_REPLY:
+    case OFPUTIL_OFPST_FLOW_REPLY:
+    case OFPUTIL_OFPST_QUEUE_REPLY:
+    case OFPUTIL_OFPST_PORT_REPLY:
+    case OFPUTIL_OFPST_TABLE_REPLY:
+    case OFPUTIL_OFPST_AGGREGATE_REPLY:
+    case OFPUTIL_NXT_STATUS_REQUEST:
+    case OFPUTIL_NXT_STATUS_REPLY:
+    case OFPUTIL_NXT_TUN_ID_FROM_COOKIE:
+    case OFPUTIL_NXT_ROLE_REQUEST:
+    case OFPUTIL_NXT_ROLE_REPLY:
+    case OFPUTIL_NXT_SET_FLOW_FORMAT:
+    case OFPUTIL_NXT_FLOW_MOD:
+    case OFPUTIL_NXT_FLOW_REMOVED:
+    case OFPUTIL_NXST_FLOW_REQUEST:
+    case OFPUTIL_NXST_AGGREGATE_REQUEST:
+    case OFPUTIL_NXST_FLOW_REPLY:
+    case OFPUTIL_NXST_AGGREGATE_REPLY:
+    default:
+        if (VLOG_IS_DBG_ENABLED()) {
+            char *s = ofp_to_string(msg->data, msg->size, 2);
+            VLOG_DBG_RL(&rl, "%016llx: OpenFlow packet ignored: %s",
+                        sw->datapath_id, s);
+            free(s);
         }
     }
-    if (VLOG_IS_DBG_ENABLED()) {
-        char *s = ofp_to_string(msg->data, msg->size, 2);
-        VLOG_DBG_RL(&rl, "%016llx: OpenFlow packet ignored: %s",
-                    sw->datapath_id, s);
-        free(s);
-    }
 }
 
 static void
@@ -280,20 +298,14 @@ queue_tx(struct lswitch *sw, struct rconn *rconn, struct ofpbuf *b)
 }
 
 static void
-process_switch_features(struct lswitch *sw, struct rconn *rconn OVS_UNUSED,
-                        void *osf_)
+process_switch_features(struct lswitch *sw, struct ofp_switch_features *osf)
 {
-    struct ofp_switch_features *osf = osf_;
     size_t n_ports;
     size_t i;
 
-    if (check_ofp_message_array(&osf->header, OFPT_FEATURES_REPLY,
-                                sizeof *osf, sizeof *osf->ports, &n_ports)) {
-        return;
-    }
-
     sw->datapath_id = ntohll(osf->datapath_id);
 
+    n_ports = (ntohs(osf->header.length) - sizeof *osf) / sizeof *osf->ports;
     for (i = 0; i < n_ports; i++) {
         struct ofp_phy_port *opp = &osf->ports[i];
         struct lswitch_port *lp;
@@ -364,9 +376,9 @@ get_queue_id(const struct lswitch *sw, uint16_t in_port)
 }
 
 static void
-process_packet_in(struct lswitch *sw, struct rconn *rconn, void *opi_)
+process_packet_in(struct lswitch *sw, struct rconn *rconn,
+                  const struct ofp_packet_in *opi)
 {
-    struct ofp_packet_in *opi = opi_;
     uint16_t in_port = ntohs(opi->in_port);
     uint32_t queue_id;
     uint16_t out_port;
@@ -388,7 +400,7 @@ process_packet_in(struct lswitch *sw, struct rconn *rconn, void *opi_)
     /* Extract flow data from 'opi' into 'flow'. */
     pkt_ofs = offsetof(struct ofp_packet_in, data);
     pkt_len = ntohs(opi->header.length) - pkt_ofs;
-    pkt.data = opi->data;
+    pkt.data = (void *) opi->data;
     pkt.size = pkt_len;
     flow_extract(&pkt, 0, in_port, &flow);
 
@@ -456,8 +468,8 @@ process_packet_in(struct lswitch *sw, struct rconn *rconn, void *opi_)
 }
 
 static void
-process_echo_request(struct lswitch *sw, struct rconn *rconn, void *rq_)
+process_echo_request(struct lswitch *sw, struct rconn *rconn,
+                     const struct ofp_header *rq)
 {
-    struct ofp_header *rq = rq_;
     queue_tx(sw, rconn, make_echo_reply(rq));
 }
diff --git a/lib/ofp-print.c b/lib/ofp-print.c
index 97fd065..88f1291 100644
--- a/lib/ofp-print.c
+++ b/lib/ofp-print.c
@@ -100,12 +100,11 @@ ofp_packet_to_string(const void *data, size_t len, size_t total_len OVS_UNUSED)
     return ds_cstr(&ds);
 }
 
-/* Pretty-print the OFPT_PACKET_IN packet of 'len' bytes at 'oh' to 'stream'
- * at the given 'verbosity' level. */
 static void
-ofp_packet_in(struct ds *string, const void *oh, size_t len, int verbosity)
+ofp_print_packet_in(struct ds *string, const struct ofp_packet_in *op,
+                    int verbosity)
 {
-    const struct ofp_packet_in *op = oh;
+    size_t len = ntohs(op->header.length);
     size_t data_len;
 
     ds_put_format(string, " total_len=%"PRIu16" in_port=",
@@ -476,12 +475,11 @@ ofp_print_actions(struct ds *string, const struct ofp_action_header *action,
     }
 }
 
-/* Pretty-print the OFPT_PACKET_OUT packet of 'len' bytes at 'oh' to 'string'
- * at the given 'verbosity' level. */
-static void ofp_packet_out(struct ds *string, const void *oh, size_t len,
-                           int verbosity)
+static void
+ofp_print_packet_out(struct ds *string, const struct ofp_packet_out *opo,
+                     int verbosity)
 {
-    const struct ofp_packet_out *opo = oh;
+    size_t len = ntohs(opo->header.length);
     size_t actions_len = ntohs(opo->actions_len);
 
     ds_put_cstr(string, " in_port=");
@@ -604,13 +602,11 @@ ofp_print_phy_port(struct ds *string, const struct ofp_phy_port *port)
     }
 }
 
-/* Pretty-print the struct ofp_switch_features of 'len' bytes at 'oh' to
- * 'string' at the given 'verbosity' level. */
 static void
-ofp_print_switch_features(struct ds *string, const void *oh, size_t len,
-                          int verbosity OVS_UNUSED)
+ofp_print_switch_features(struct ds *string,
+                          const struct ofp_switch_features *osf)
 {
-    const struct ofp_switch_features *osf = oh;
+    size_t len = ntohs(osf->header.length);
     struct ofp_phy_port *port_list;
     int n_ports;
     int i;
@@ -622,9 +618,6 @@ ofp_print_switch_features(struct ds *string, const void *oh, size_t len,
     ds_put_format(string, "features: capabilities:%#x, actions:%#x\n",
            ntohl(osf->capabilities), ntohl(osf->actions));
 
-    if (ntohs(osf->header.length) >= sizeof *osf) {
-        len = MIN(len, ntohs(osf->header.length));
-    }
     n_ports = (len - sizeof *osf) / sizeof *osf->ports;
 
     port_list = xmemdup(osf->ports, len - sizeof *osf);
@@ -635,13 +628,9 @@ ofp_print_switch_features(struct ds *string, const void *oh, size_t len,
     free(port_list);
 }
 
-/* Pretty-print the struct ofp_switch_config of 'len' bytes at 'oh' to 'string'
- * at the given 'verbosity' level. */
 static void
-ofp_print_switch_config(struct ds *string, const void *oh,
-                        size_t len OVS_UNUSED, int verbosity OVS_UNUSED)
+ofp_print_switch_config(struct ds *string, const struct ofp_switch_config *osc)
 {
-    const struct ofp_switch_config *osc = oh;
     uint16_t flags;
 
     flags = ntohs(osc->flags);
@@ -780,13 +769,11 @@ ofp_match_to_string(const struct ofp_match *om, int verbosity)
     return ds_cstr(&f);
 }
 
-/* Pretty-print the OFPT_FLOW_MOD packet of 'len' bytes at 'oh' to 'string'
- * at the given 'verbosity' level. */
 static void
-ofp_print_flow_mod(struct ds *string, const void *oh, size_t len,
+ofp_print_flow_mod(struct ds *string, const struct ofp_flow_mod *ofm,
                    int verbosity)
 {
-    const struct ofp_flow_mod *ofm = oh;
+    size_t len = ntohs(ofm->header.length);
 
     ds_put_char(string, ' ');
     ofp_print_match(string, &ofm->match, verbosity);
@@ -837,14 +824,10 @@ ofp_print_flow_mod(struct ds *string, const void *oh, size_t len,
     ds_put_char(string, '\n');
 }
 
-/* Pretty-print the OFPT_FLOW_REMOVED packet of 'len' bytes at 'oh' to 'string'
- * at the given 'verbosity' level. */
 static void
-ofp_print_flow_removed(struct ds *string, const void *oh,
-                       size_t len OVS_UNUSED, int verbosity)
+ofp_print_flow_removed(struct ds *string, const struct ofp_flow_removed *ofr,
+                       int verbosity)
 {
-    const struct ofp_flow_removed *ofr = oh;
-
     ofp_print_match(string, &ofr->match, verbosity);
     ds_put_cstr(string, " reason=");
     switch (ofr->reason) {
@@ -876,11 +859,8 @@ ofp_print_flow_removed(struct ds *string, const void *oh,
 }
 
 static void
-ofp_print_port_mod(struct ds *string, const void *oh, size_t len OVS_UNUSED,
-                   int verbosity OVS_UNUSED)
+ofp_print_port_mod(struct ds *string, const struct ofp_port_mod *opm)
 {
-    const struct ofp_port_mod *opm = oh;
-
     ds_put_format(string, "port: %d: addr:"ETH_ADDR_FMT", config: %#x, mask:%#x\n",
             ntohs(opm->port_no), ETH_ADDR_ARGS(opm->hw_addr),
             ntohl(opm->config), ntohl(opm->mask));
@@ -965,13 +945,10 @@ lookup_error_code(int type, int code)
     return "?";
 }
 
-/* Pretty-print the OFPT_ERROR packet of 'len' bytes at 'oh' to 'string'
- * at the given 'verbosity' level. */
 static void
-ofp_print_error_msg(struct ds *string, const void *oh, size_t len,
-                       int verbosity OVS_UNUSED)
+ofp_print_error_msg(struct ds *string, const struct ofp_error_msg *oem)
 {
-    const struct ofp_error_msg *oem = oh;
+    size_t len = ntohs(oem->header.length);
     int type = ntohs(oem->type);
     int code = ntohs(oem->code);
     char *s;
@@ -997,14 +974,9 @@ ofp_print_error_msg(struct ds *string, const void *oh, size_t len,
     }
 }
 
-/* Pretty-print the OFPT_PORT_STATUS packet of 'len' bytes at 'oh' to 'string'
- * at the given 'verbosity' level. */
 static void
-ofp_print_port_status(struct ds *string, const void *oh, size_t len OVS_UNUSED,
-                      int verbosity OVS_UNUSED)
+ofp_print_port_status(struct ds *string, const struct ofp_port_status *ops)
 {
-    const struct ofp_port_status *ops = oh;
-
     if (ops->reason == OFPPR_ADD) {
         ds_put_format(string, " ADD:");
     } else if (ops->reason == OFPPR_DELETE) {
@@ -1017,10 +989,9 @@ ofp_print_port_status(struct ds *string, const void *oh, size_t len OVS_UNUSED,
 }
 
 static void
-ofp_desc_stats_reply(struct ds *string, const void *body,
-                     size_t len OVS_UNUSED, int verbosity OVS_UNUSED)
+ofp_print_ofpst_desc_reply(struct ds *string, const struct ofp_header *oh)
 {
-    const struct ofp_desc_stats *ods = body;
+    const struct ofp_desc_stats *ods = ofputil_stats_body(oh);
 
     ds_put_format(string, "Manufacturer: %.*s\n",
             (int) sizeof ods->mfr_desc, ods->mfr_desc);
@@ -1035,10 +1006,10 @@ ofp_desc_stats_reply(struct ds *string, const void *body,
 }
 
 static void
-ofp_flow_stats_request(struct ds *string, const void *oh,
-                       size_t len OVS_UNUSED, int verbosity)
+ofp_print_ofpst_flow_request(struct ds *string, const struct ofp_header *oh,
+                             int verbosity)
 {
-    const struct ofp_flow_stats_request *fsr = oh;
+    const struct ofp_flow_stats_request *fsr = ofputil_stats_body(oh);
 
     if (fsr->table_id == 0xff) {
         ds_put_format(string, " table_id=any, ");
@@ -1050,10 +1021,11 @@ ofp_flow_stats_request(struct ds *string, const void *oh,
 }
 
 static void
-ofp_flow_stats_reply(struct ds *string, const void *body_, size_t len,
-                     int verbosity)
+ofp_print_ofpst_flow_reply(struct ds *string, const struct ofp_header *oh,
+                           int verbosity)
 {
-    const char *body = body_;
+    size_t len = ntohs(oh->length);
+    const char *body = ofputil_stats_body(oh);
     const char *pos = body;
     for (;;) {
         const struct ofp_flow_stats *fs;
@@ -1116,10 +1088,10 @@ ofp_flow_stats_reply(struct ds *string, const void *body_, size_t len,
 }
 
 static void
-ofp_aggregate_stats_request(struct ds *string, const void *oh,
-                            size_t len OVS_UNUSED, int verbosity)
+ofp_print_ofpst_aggregate_request(struct ds *string,
+                                  const struct ofp_header *oh, int verbosity)
 {
-    const struct ofp_aggregate_stats_request *asr = oh;
+    const struct ofp_aggregate_stats_request *asr = ofputil_stats_body(oh);
 
     if (asr->table_id == 0xff) {
         ds_put_format(string, " table_id=any, ");
@@ -1131,10 +1103,9 @@ ofp_aggregate_stats_request(struct ds *string, const void *oh,
 }
 
 static void
-ofp_aggregate_stats_reply(struct ds *string, const void *body_,
-                          size_t len OVS_UNUSED, int verbosity OVS_UNUSED)
+ofp_print_ofpst_aggregate_reply(struct ds *string, const struct ofp_header *oh)
 {
-    const struct ofp_aggregate_stats_reply *asr = body_;
+    const struct ofp_aggregate_stats_reply *asr = ofputil_stats_body(oh);
 
     ds_put_format(string, " packet_count=%"PRIu64, ntohll(asr->packet_count));
     ds_put_format(string, " byte_count=%"PRIu64, ntohll(asr->byte_count));
@@ -1158,19 +1129,18 @@ static void print_port_stat(struct ds *string, const char *leader,
 }
 
 static void
-ofp_port_stats_request(struct ds *string, const void *body_,
-                       size_t len OVS_UNUSED, int verbosity OVS_UNUSED)
+ofp_print_ofpst_port_request(struct ds *string, const struct ofp_header *oh)
 {
-    const struct ofp_port_stats_request *psr = body_;
+    const struct ofp_port_stats_request *psr = ofputil_stats_body(oh);
     ds_put_format(string, "port_no=%"PRIu16, ntohs(psr->port_no));
 }
 
 static void
-ofp_port_stats_reply(struct ds *string, const void *body, size_t len,
-                     int verbosity)
+ofp_print_ofpst_port_reply(struct ds *string, const struct ofp_header *oh,
+                           int verbosity)
 {
-    const struct ofp_port_stats *ps = body;
-    size_t n = len / sizeof *ps;
+    const struct ofp_port_stats *ps = ofputil_stats_body(oh);
+    size_t n = ntohs(oh->length) / sizeof *ps;
     ds_put_format(string, " %zu ports\n", n);
     if (verbosity < 1) {
         return;
@@ -1198,11 +1168,11 @@ ofp_port_stats_reply(struct ds *string, const void *body, size_t len,
 }
 
 static void
-ofp_table_stats_reply(struct ds *string, const void *body, size_t len,
-                     int verbosity)
+ofp_print_ofpst_table_reply(struct ds *string, const struct ofp_header *oh,
+                            int verbosity)
 {
-    const struct ofp_table_stats *ts = body;
-    size_t n = len / sizeof *ts;
+    const struct ofp_table_stats *ts = ofputil_stats_body(oh);
+    size_t n = ntohs(oh->length) / sizeof *ts;
     ds_put_format(string, " %zu tables\n", n);
     if (verbosity < 1) {
         return;
@@ -1236,10 +1206,9 @@ ofp_print_queue_name(struct ds *string, uint32_t queue_id)
 }
 
 static void
-ofp_queue_stats_request(struct ds *string, const void *body_,
-                       size_t len OVS_UNUSED, int verbosity OVS_UNUSED)
+ofp_print_ofpst_queue_request(struct ds *string, const struct ofp_header *oh)
 {
-    const struct ofp_queue_stats_request *qsr = body_;
+    const struct ofp_queue_stats_request *qsr = ofputil_stats_body(oh);
 
     ds_put_cstr(string, "port=");
     ofp_print_port_name(string, ntohs(qsr->port_no));
@@ -1249,11 +1218,11 @@ ofp_queue_stats_request(struct ds *string, const void *body_,
 }
 
 static void
-ofp_queue_stats_reply(struct ds *string, const void *body, size_t len,
-                     int verbosity)
+ofp_print_ofpst_queue_reply(struct ds *string, const struct ofp_header *oh,
+                            int verbosity)
 {
-    const struct ofp_queue_stats *qs = body;
-    size_t n = len / sizeof *qs;
+    const struct ofp_queue_stats *qs = ofputil_stats_body(oh);
+    size_t n = ntohs(oh->length) / sizeof *qs;
     ds_put_format(string, " %zu queues\n", n);
     if (verbosity < 1) {
         return;
@@ -1273,300 +1242,196 @@ ofp_queue_stats_reply(struct ds *string, const void *body, size_t len,
 }
 
 static void
-vendor_stat(struct ds *string, const void *body, size_t len,
-            int verbosity OVS_UNUSED)
-{
-    ds_put_format(string, " vendor=%08"PRIx32, ntohl(*(uint32_t *) body));
-    ds_put_format(string, " %zu bytes additional data",
-                  len - sizeof(uint32_t));
-}
-
-enum stats_direction {
-    REQUEST,
-    REPLY
-};
-
-static void
-print_stats(struct ds *string, int type, const void *body, size_t body_len,
-            int verbosity, enum stats_direction direction)
+ofp_print_stats_request(struct ds *string, const struct ofp_header *oh)
 {
-    struct stats_msg {
-        size_t min_body, max_body;
-        void (*printer)(struct ds *, const void *, size_t len, int verbosity);
-    };
-
-    struct stats_type {
-        int type;
-        const char *name;
-        struct stats_msg request;
-        struct stats_msg reply;
-    };
-
-    static const struct stats_type stats_types[] = {
-        {
-            OFPST_DESC,
-            "description",
-            { 0, 0, NULL },
-            { 0, SIZE_MAX, ofp_desc_stats_reply },
-        },
-        {
-            OFPST_FLOW,
-            "flow",
-            { sizeof(struct ofp_flow_stats_request),
-              sizeof(struct ofp_flow_stats_request),
-              ofp_flow_stats_request },
-            { 0, SIZE_MAX, ofp_flow_stats_reply },
-        },
-        {
-            OFPST_AGGREGATE,
-            "aggregate",
-            { sizeof(struct ofp_aggregate_stats_request),
-              sizeof(struct ofp_aggregate_stats_request),
-              ofp_aggregate_stats_request },
-            { sizeof(struct ofp_aggregate_stats_reply),
-              sizeof(struct ofp_aggregate_stats_reply),
-              ofp_aggregate_stats_reply },
-        },
-        {
-            OFPST_TABLE,
-            "table",
-            { 0, 0, NULL },
-            { 0, SIZE_MAX, ofp_table_stats_reply },
-        },
-        {
-            OFPST_PORT,
-            "port",
-            { sizeof(struct ofp_port_stats_request),
-              sizeof(struct ofp_port_stats_request),
-              ofp_port_stats_request },
-            { 0, SIZE_MAX, ofp_port_stats_reply },
-        },
-        {
-            OFPST_QUEUE,
-            "queue",
-            { sizeof(struct ofp_queue_stats_request),
-              sizeof(struct ofp_queue_stats_request),
-              ofp_queue_stats_request },
-            { 0, SIZE_MAX, ofp_queue_stats_reply },
-        },
-        {
-            OFPST_VENDOR,
-            "vendor-specific",
-            { sizeof(uint32_t), SIZE_MAX, vendor_stat },
-            { sizeof(uint32_t), SIZE_MAX, vendor_stat },
-        },
-        {
-            -1,
-            "unknown",
-            { 0, 0, NULL, },
-            { 0, 0, NULL, },
-        },
-    };
-
-    const struct stats_type *s;
-    const struct stats_msg *m;
-
-    if (type >= ARRAY_SIZE(stats_types) || !stats_types[type].name) {
-        ds_put_format(string, " ***unknown type %d***", type);
-        return;
-    }
-    for (s = stats_types; s->type >= 0; s++) {
-        if (s->type == type) {
-            break;
-        }
-    }
-    ds_put_format(string, " type=%d(%s)\n", type, s->name);
-
-    m = direction == REQUEST ? &s->request : &s->reply;
-    if (body_len < m->min_body || body_len > m->max_body) {
-        ds_put_format(string, " ***body_len=%zu not in %zu...%zu***",
-                      body_len, m->min_body, m->max_body);
-        return;
-    }
-    if (m->printer) {
-        m->printer(string, body, body_len, verbosity);
-    }
-}
-
-static void
-ofp_stats_request(struct ds *string, const void *oh, size_t len, int verbosity)
-{
-    const struct ofp_stats_request *srq = oh;
+    const struct ofp_stats_request *srq
+        = (const struct ofp_stats_request *) oh;
 
     if (srq->flags) {
         ds_put_format(string, " ***unknown flags 0x%04"PRIx16"***",
                       ntohs(srq->flags));
     }
-
-    print_stats(string, ntohs(srq->type), srq->body,
-                len - offsetof(struct ofp_stats_request, body),
-                verbosity, REQUEST);
 }
 
 static void
-ofp_stats_reply(struct ds *string, const void *oh, size_t len, int verbosity)
+ofp_print_stats_reply(struct ds *string, const struct ofp_header *oh)
 {
-    const struct ofp_stats_reply *srp = oh;
+    const struct ofp_stats_reply *srp = (const struct ofp_stats_reply *) oh;
 
-    ds_put_cstr(string, " flags=");
-    if (!srp->flags) {
-        ds_put_cstr(string, "none");
-    } else {
+    if (srp->flags) {
         uint16_t flags = ntohs(srp->flags);
+
+        ds_put_cstr(string, " flags=");
         if (flags & OFPSF_REPLY_MORE) {
             ds_put_cstr(string, "[more]");
             flags &= ~OFPSF_REPLY_MORE;
         }
         if (flags) {
-            ds_put_format(string, "[***unknown flags 0x%04"PRIx16"***]", flags);
+            ds_put_format(string, "[***unknown flags 0x%04"PRIx16"***]",
+                          flags);
         }
     }
-
-    print_stats(string, ntohs(srp->type), srp->body,
-                len - offsetof(struct ofp_stats_reply, body),
-                verbosity, REPLY);
 }
 
 static void
-ofp_echo(struct ds *string, const void *oh, size_t len, int verbosity)
+ofp_print_echo(struct ds *string, const struct ofp_header *oh, int verbosity)
 {
-    const struct ofp_header *hdr = oh;
+    size_t len = ntohs(oh->length);
 
-    ds_put_format(string, " %zu bytes of payload\n", len - sizeof *hdr);
+    ds_put_format(string, " %zu bytes of payload\n", len - sizeof *oh);
     if (verbosity > 1) {
-        ds_put_hex_dump(string, hdr, len - sizeof *hdr, 0, true);
+        ds_put_hex_dump(string, oh + 1, len - sizeof *oh, 0, true);
     }
 }
 
-struct openflow_packet {
-    uint8_t type;
-    const char *name;
-    size_t min_size;
-    void (*printer)(struct ds *, const void *, size_t len, int verbosity);
-};
+static void
+ofp_to_string__(const struct ofp_header *oh,
+                const struct ofputil_msg_type *type, struct ds *string,
+                int verbosity)
+{
+    const void *msg = oh;
+
+    ds_put_format(string, "%s (xid=0x%"PRIx32"):",
+                  ofputil_msg_type_name(type), ntohl(oh->xid));
+
+    switch (ofputil_msg_type_code(type)) {
+    case OFPUTIL_INVALID:
+        break;
+
+    case OFPUTIL_OFPT_HELLO:
+        break;
+
+    case OFPUTIL_OFPT_ERROR:
+        ofp_print_error_msg(string, msg);
+        break;
+
+    case OFPUTIL_OFPT_ECHO_REQUEST:
+    case OFPUTIL_OFPT_ECHO_REPLY:
+        ofp_print_echo(string, oh, verbosity);
+        break;
+
+    case OFPUTIL_OFPT_FEATURES_REQUEST:
+        break;
+
+    case OFPUTIL_OFPT_FEATURES_REPLY:
+        ofp_print_switch_features(string, msg);
+        break;
+
+    case OFPUTIL_OFPT_GET_CONFIG_REQUEST:
+        break;
+
+    case OFPUTIL_OFPT_GET_CONFIG_REPLY:
+    case OFPUTIL_OFPT_SET_CONFIG:
+        ofp_print_switch_config(string, msg);
+        break;
+
+    case OFPUTIL_OFPT_PACKET_IN:
+        ofp_print_packet_in(string, msg, verbosity);
+        break;
+
+    case OFPUTIL_OFPT_FLOW_REMOVED:
+        ofp_print_flow_removed(string, msg, verbosity);
+        break;
+
+    case OFPUTIL_OFPT_PORT_STATUS:
+        ofp_print_port_status(string, msg);
+        break;
+
+    case OFPUTIL_OFPT_PACKET_OUT:
+        ofp_print_packet_out(string, msg, verbosity);
+        break;
+
+    case OFPUTIL_OFPT_FLOW_MOD:
+        ofp_print_flow_mod(string, msg, verbosity);
+        break;
+
+    case OFPUTIL_OFPT_PORT_MOD:
+        ofp_print_port_mod(string, msg);
+        break;
+
+    case OFPUTIL_OFPT_BARRIER_REQUEST:
+    case OFPUTIL_OFPT_BARRIER_REPLY:
+        break;
+
+    case OFPUTIL_OFPT_QUEUE_GET_CONFIG_REQUEST:
+    case OFPUTIL_OFPT_QUEUE_GET_CONFIG_REPLY:
+        /* XXX */
+        break;
+
+    case OFPUTIL_OFPST_DESC_REQUEST:
+        ofp_print_stats_request(string, oh);
+        break;
+
+    case OFPUTIL_OFPST_FLOW_REQUEST:
+        ofp_print_stats_request(string, oh);
+        ofp_print_ofpst_flow_request(string, oh, verbosity);
+        break;
+
+    case OFPUTIL_OFPST_AGGREGATE_REQUEST:
+        ofp_print_stats_request(string, oh);
+        ofp_print_ofpst_aggregate_request(string, oh, verbosity);
+        break;
+
+    case OFPUTIL_OFPST_TABLE_REQUEST:
+        ofp_print_stats_request(string, oh);
+        break;
+
+    case OFPUTIL_OFPST_PORT_REQUEST:
+        ofp_print_stats_request(string, oh);
+        ofp_print_ofpst_port_request(string, oh);
+        break;
+
+    case OFPUTIL_OFPST_QUEUE_REQUEST:
+        ofp_print_stats_request(string, oh);
+        ofp_print_ofpst_queue_request(string, oh);
+        break;
+
+    case OFPUTIL_OFPST_DESC_REPLY:
+        ofp_print_stats_reply(string, oh);
+        ofp_print_ofpst_desc_reply(string, oh);
+        break;
+
+    case OFPUTIL_OFPST_FLOW_REPLY:
+        ofp_print_stats_reply(string, oh);
+        ofp_print_ofpst_flow_reply(string, oh, verbosity);
+        break;
+
+    case OFPUTIL_OFPST_QUEUE_REPLY:
+        ofp_print_stats_reply(string, oh);
+        ofp_print_ofpst_queue_reply(string, oh, verbosity);
+        break;
+
+    case OFPUTIL_OFPST_PORT_REPLY:
+        ofp_print_stats_reply(string, oh);
+        ofp_print_ofpst_port_reply(string, oh, verbosity);
+        break;
+
+    case OFPUTIL_OFPST_TABLE_REPLY:
+        ofp_print_stats_reply(string, oh);
+        ofp_print_ofpst_table_reply(string, oh, verbosity);
+        break;
+
+    case OFPUTIL_OFPST_AGGREGATE_REPLY:
+        ofp_print_stats_reply(string, oh);
+        ofp_print_ofpst_aggregate_reply(string, oh);
+        break;
 
-static const struct openflow_packet packets[] = {
-    {
-        OFPT_HELLO,
-        "hello",
-        sizeof (struct ofp_header),
-        NULL,
-    },
-    {
-        OFPT_FEATURES_REQUEST,
-        "features_request",
-        sizeof (struct ofp_header),
-        NULL,
-    },
-    {
-        OFPT_FEATURES_REPLY,
-        "features_reply",
-        sizeof (struct ofp_switch_features),
-        ofp_print_switch_features,
-    },
-    {
-        OFPT_GET_CONFIG_REQUEST,
-        "get_config_request",
-        sizeof (struct ofp_header),
-        NULL,
-    },
-    {
-        OFPT_GET_CONFIG_REPLY,
-        "get_config_reply",
-        sizeof (struct ofp_switch_config),
-        ofp_print_switch_config,
-    },
-    {
-        OFPT_SET_CONFIG,
-        "set_config",
-        sizeof (struct ofp_switch_config),
-        ofp_print_switch_config,
-    },
-    {
-        OFPT_PACKET_IN,
-        "packet_in",
-        offsetof(struct ofp_packet_in, data),
-        ofp_packet_in,
-    },
-    {
-        OFPT_PACKET_OUT,
-        "packet_out",
-        sizeof (struct ofp_packet_out),
-        ofp_packet_out,
-    },
-    {
-        OFPT_FLOW_MOD,
-        "flow_mod",
-        sizeof (struct ofp_flow_mod),
-        ofp_print_flow_mod,
-    },
-    {
-        OFPT_FLOW_REMOVED,
-        "flow_removed",
-        sizeof (struct ofp_flow_removed),
-        ofp_print_flow_removed,
-    },
-    {
-        OFPT_PORT_MOD,
-        "port_mod",
-        sizeof (struct ofp_port_mod),
-        ofp_print_port_mod,
-    },
-    {
-        OFPT_PORT_STATUS,
-        "port_status",
-        sizeof (struct ofp_port_status),
-        ofp_print_port_status
-    },
-    {
-        OFPT_ERROR,
-        "error_msg",
-        sizeof (struct ofp_error_msg),
-        ofp_print_error_msg,
-    },
-    {
-        OFPT_STATS_REQUEST,
-        "stats_request",
-        sizeof (struct ofp_stats_request),
-        ofp_stats_request,
-    },
-    {
-        OFPT_STATS_REPLY,
-        "stats_reply",
-        sizeof (struct ofp_stats_reply),
-        ofp_stats_reply,
-    },
-    {
-        OFPT_ECHO_REQUEST,
-        "echo_request",
-        sizeof (struct ofp_header),
-        ofp_echo,
-    },
-    {
-        OFPT_ECHO_REPLY,
-        "echo_reply",
-        sizeof (struct ofp_header),
-        ofp_echo,
-    },
-    {
-        OFPT_VENDOR,
-        "vendor",
-        sizeof (struct ofp_vendor_header),
-        NULL,
-    },
-    {
-        OFPT_BARRIER_REQUEST,
-        "barrier_request",
-        sizeof (struct ofp_header),
-        NULL,
-    },
-    {
-        OFPT_BARRIER_REPLY,
-        "barrier_reply",
-        sizeof (struct ofp_header),
-        NULL,
+    case OFPUTIL_NXT_STATUS_REQUEST:
+    case OFPUTIL_NXT_STATUS_REPLY:
+    case OFPUTIL_NXT_TUN_ID_FROM_COOKIE:
+    case OFPUTIL_NXT_ROLE_REQUEST:
+    case OFPUTIL_NXT_ROLE_REPLY:
+    case OFPUTIL_NXT_SET_FLOW_FORMAT:
+    case OFPUTIL_NXT_FLOW_MOD:
+    case OFPUTIL_NXT_FLOW_REMOVED:
+    case OFPUTIL_NXST_FLOW_REQUEST:
+    case OFPUTIL_NXST_AGGREGATE_REQUEST:
+    case OFPUTIL_NXST_FLOW_REPLY:
+    case OFPUTIL_NXST_AGGREGATE_REPLY:
+        /* XXX */
+        break;
     }
-};
+}
 
 /* Composes and returns a string representing the OpenFlow packet of 'len'
  * bytes at 'oh' at the given 'verbosity' level.  0 is a minimal amount of
@@ -1577,58 +1442,45 @@ ofp_to_string(const void *oh_, size_t len, int verbosity)
 {
     struct ds string = DS_EMPTY_INITIALIZER;
     const struct ofp_header *oh = oh_;
-    const struct openflow_packet *pkt;
 
     if (len < sizeof(struct ofp_header)) {
         ds_put_cstr(&string, "OpenFlow packet too short:\n");
-        ds_put_hex_dump(&string, oh, len, 0, true);
-        return ds_cstr(&string);
     } else if (oh->version != OFP_VERSION) {
-        ds_put_format(&string, "Bad OpenFlow version %"PRIu8":\n", oh->version);
-        ds_put_hex_dump(&string, oh, len, 0, true);
-        return ds_cstr(&string);
-    }
-
-    for (pkt = packets; ; pkt++) {
-        if (pkt >= &packets[ARRAY_SIZE(packets)]) {
-            ds_put_format(&string, "Unknown OpenFlow packet type %"PRIu8":\n",
-                          oh->type);
-            ds_put_hex_dump(&string, oh, len, 0, true);
-            return ds_cstr(&string);
-        } else if (oh->type == pkt->type) {
-            break;
+        ds_put_format(&string, "Bad OpenFlow version %"PRIu8":\n",
+                      oh->version);
+    } else if (ntohs(oh->length) > len) {
+        ds_put_format(&string,
+                      "(***truncated to %zu bytes from %"PRIu16"***)",
+                      len, ntohs(oh->length));
+    } else if (ntohs(oh->length) < len) {
+        ds_put_format(&string,
+                      "(***only uses %"PRIu16" bytes out of %zu***)\n",
+                      ntohs(oh->length), len);
+    } else {
+        const struct ofputil_msg_type *type;
+        int err_type, err_code;
+        int error;
+
+        error = ofputil_decode_msg_type(oh, &type);
+        if (!error) {
+            ofp_to_string__(oh, type, &string, verbosity);
+            if (verbosity >= 3) {
+                ds_put_hex_dump(&string, oh, len, 0, true);
+            }
+            if (string.string[string.length - 1] != '\n') {
+                ds_put_char(&string, '\n');
+            }
+            return ds_steal_cstr(&string);
         }
-    }
-
-    ds_put_format(&string, "%s (xid=0x%"PRIx32"):", pkt->name, ntohl(oh->xid));
 
-    if (ntohs(oh->length) > len)
-        ds_put_format(&string, " (***truncated to %zu bytes from %"PRIu16"***)",
-                len, ntohs(oh->length));
-    else if (ntohs(oh->length) < len) {
-        ds_put_format(&string, " (***only uses %"PRIu16" bytes out of %zu***)\n",
-                ntohs(oh->length), len);
-        len = ntohs(oh->length);
-    }
-
-    if (len < pkt->min_size) {
-        ds_put_format(&string, " (***length=%zu < min_size=%zu***)\n",
-                len, pkt->min_size);
-    } else if (!pkt->printer) {
-        if (len > sizeof *oh) {
-            ds_put_format(&string, " length=%"PRIu16" (decoder not implemented)\n",
-                          ntohs(oh->length));
-        }
-    } else {
-        pkt->printer(&string, oh, len, verbosity);
-    }
-    if (verbosity >= 3) {
-        ds_put_hex_dump(&string, oh, len, 0, true);
-    }
-    if (string.string[string.length - 1] != '\n') {
-        ds_put_char(&string, '\n');
+        err_type = get_ofp_err_type(error);
+        err_code = get_ofp_err_code(error);
+        ds_put_format(&string, "Bad OpenFlow message (%s, %s)\n",
+                      lookup_error_type(err_type),
+                      lookup_error_code(err_type, err_code));
     }
-    return ds_cstr(&string);
+    ds_put_hex_dump(&string, oh, len, 0, true);
+    return ds_steal_cstr(&string);
 }
 
 /* Returns the name for the specified OpenFlow message type as a string,
@@ -1639,23 +1491,81 @@ ofp_to_string(const void *oh_, size_t len, int verbosity)
 char *
 ofp_message_type_to_string(uint8_t type)
 {
-    struct ds s = DS_EMPTY_INITIALIZER;
-    const struct openflow_packet *pkt;
-    for (pkt = packets; ; pkt++) {
-        if (pkt >= &packets[ARRAY_SIZE(packets)]) {
-            ds_put_format(&s, "0x%02"PRIx8, type);
-            break;
-        } else if (type == pkt->type) {
-            const char *p;
+    const char *name;
 
-            ds_put_cstr(&s, "OFPT_");
-            for (p = pkt->name; *p; p++) {
-                ds_put_char(&s, toupper((unsigned char) *p));
-            }
-            break;
-        }
+    switch (type) {
+    case OFPT_HELLO:
+        name = "HELLO";
+        break;
+    case OFPT_ERROR:
+        name = "ERROR";
+        break;
+    case OFPT_ECHO_REQUEST:
+        name = "ECHO_REQUEST";
+        break;
+    case OFPT_ECHO_REPLY:
+        name = "ECHO_REPLY";
+        break;
+    case OFPT_VENDOR:
+        name = "VENDOR";
+        break;
+    case OFPT_FEATURES_REQUEST:
+        name = "FEATURES_REQUEST";
+        break;
+    case OFPT_FEATURES_REPLY:
+        name = "FEATURES_REPLY";
+        break;
+    case OFPT_GET_CONFIG_REQUEST:
+        name = "GET_CONFIG_REQUEST";
+        break;
+    case OFPT_GET_CONFIG_REPLY:
+        name = "GET_CONFIG_REPLY";
+        break;
+    case OFPT_SET_CONFIG:
+        name = "SET_CONFIG";
+        break;
+    case OFPT_PACKET_IN:
+        name = "PACKET_IN";
+        break;
+    case OFPT_FLOW_REMOVED:
+        name = "FLOW_REMOVED";
+        break;
+    case OFPT_PORT_STATUS:
+        name = "PORT_STATUS";
+        break;
+    case OFPT_PACKET_OUT:
+        name = "PACKET_OUT";
+        break;
+    case OFPT_FLOW_MOD:
+        name = "FLOW_MOD";
+        break;
+    case OFPT_PORT_MOD:
+        name = "PORT_MOD";
+        break;
+    case OFPT_STATS_REQUEST:
+        name = "STATS_REQUEST";
+        break;
+    case OFPT_STATS_REPLY:
+        name = "STATS_REPLY";
+        break;
+    case OFPT_BARRIER_REQUEST:
+        name = "BARRIER_REQUEST";
+        break;
+    case OFPT_BARRIER_REPLY:
+        name = "BARRIER_REPLY";
+        break;
+    case OFPT_QUEUE_GET_CONFIG_REQUEST:
+        name = "QUEUE_GET_CONFIG_REQUEST";
+        break;
+    case OFPT_QUEUE_GET_CONFIG_REPLY:
+        name = "QUEUE_GET_CONFIG_REPLY";
+        break;
+    default:
+        name = NULL;
+        break;
     }
-    return ds_cstr(&s);
+
+    return name ? xasprintf("OFPT_%s", name) : xasprintf("0x%02"PRIx8, type);
 }
 
 static void
diff --git a/lib/ofp-util.c b/lib/ofp-util.c
index 99ef722..375ab11 100644
--- a/lib/ofp-util.c
+++ b/lib/ofp-util.c
@@ -270,7 +270,498 @@ alloc_xid(void)
     static uint32_t next_xid = 1;
     return htonl(next_xid++);
 }
+
+/* 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 request");
+        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.
+ *
+ * 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, followed by "request" or "reply" for
+ * statistics only, e.g. "OFPT_HELLO" or "OFPST_AGGREGATE reply". */
+const char *
+ofputil_msg_type_name(const struct ofputil_msg_type *type)
+{
+    return type->name;
+}
+
 /* 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
@@ -384,6 +875,15 @@ update_openflow_length(struct ofpbuf *buffer)
     oh->length = htons(buffer->size);
 }
 
+/* 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;
+}
+
 struct ofpbuf *
 make_flow_mod(uint16_t command, const struct cls_rule *rule,
               size_t actions_len)
@@ -544,98 +1044,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)
diff --git a/lib/ofp-util.h b/lib/ofp-util.h
index 02096b0..bc01766 100644
--- a/lib/ofp-util.h
+++ b/lib/ofp-util.h
@@ -21,17 +21,83 @@
 #include <stdbool.h>
 #include <stddef.h>
 #include <stdint.h>
+#include "classifier.h"
 #include "flow.h"
 #include "openflow/nicira-ext.h"
 #include "openvswitch/types.h"
 
 struct cls_rule;
 struct ofpbuf;
-struct ofp_action_header;
 
 /* Alignment of ofp_actions. */
 #define OFP_ACTION_ALIGN 8
 
+/* Basic decoding and length validation of OpenFlow messages. */
+enum ofputil_msg_code {
+    OFPUTIL_INVALID,
+
+    /* OFPT_* messages. */
+    OFPUTIL_OFPT_HELLO,
+    OFPUTIL_OFPT_ERROR,
+    OFPUTIL_OFPT_ECHO_REQUEST,
+    OFPUTIL_OFPT_ECHO_REPLY,
+    OFPUTIL_OFPT_FEATURES_REQUEST,
+    OFPUTIL_OFPT_FEATURES_REPLY,
+    OFPUTIL_OFPT_GET_CONFIG_REQUEST,
+    OFPUTIL_OFPT_GET_CONFIG_REPLY,
+    OFPUTIL_OFPT_SET_CONFIG,
+    OFPUTIL_OFPT_PACKET_IN,
+    OFPUTIL_OFPT_FLOW_REMOVED,
+    OFPUTIL_OFPT_PORT_STATUS,
+    OFPUTIL_OFPT_PACKET_OUT,
+    OFPUTIL_OFPT_FLOW_MOD,
+    OFPUTIL_OFPT_PORT_MOD,
+    OFPUTIL_OFPT_BARRIER_REQUEST,
+    OFPUTIL_OFPT_BARRIER_REPLY,
+    OFPUTIL_OFPT_QUEUE_GET_CONFIG_REQUEST,
+    OFPUTIL_OFPT_QUEUE_GET_CONFIG_REPLY,
+
+    /* OFPST_* stat requests. */
+    OFPUTIL_OFPST_DESC_REQUEST,
+    OFPUTIL_OFPST_FLOW_REQUEST,
+    OFPUTIL_OFPST_AGGREGATE_REQUEST,
+    OFPUTIL_OFPST_TABLE_REQUEST,
+    OFPUTIL_OFPST_PORT_REQUEST,
+    OFPUTIL_OFPST_QUEUE_REQUEST,
+
+    /* OFPST_* stat replies. */
+    OFPUTIL_OFPST_DESC_REPLY,
+    OFPUTIL_OFPST_FLOW_REPLY,
+    OFPUTIL_OFPST_QUEUE_REPLY,
+    OFPUTIL_OFPST_PORT_REPLY,
+    OFPUTIL_OFPST_TABLE_REPLY,
+    OFPUTIL_OFPST_AGGREGATE_REPLY,
+
+    /* NXT_* messages. */
+    OFPUTIL_NXT_STATUS_REQUEST,
+    OFPUTIL_NXT_STATUS_REPLY,
+    OFPUTIL_NXT_TUN_ID_FROM_COOKIE,
+    OFPUTIL_NXT_ROLE_REQUEST,
+    OFPUTIL_NXT_ROLE_REPLY,
+    OFPUTIL_NXT_SET_FLOW_FORMAT,
+    OFPUTIL_NXT_FLOW_MOD,
+    OFPUTIL_NXT_FLOW_REMOVED,
+
+    /* NXST_* stat requests. */
+    OFPUTIL_NXST_FLOW_REQUEST,
+    OFPUTIL_NXST_AGGREGATE_REQUEST,
+
+    /* NXST_* stat replies. */
+    OFPUTIL_NXST_FLOW_REPLY,
+    OFPUTIL_NXST_AGGREGATE_REPLY
+};
+
+struct ofputil_msg_type;
+int ofputil_decode_msg_type(const struct ofp_header *,
+                            const struct ofputil_msg_type **);
+enum ofputil_msg_code ofputil_msg_type_code(const struct ofputil_msg_type *);
+const char *ofputil_msg_type_name(const struct ofputil_msg_type *);
+
 /* Converting OFPFW_NW_SRC_MASK and OFPFW_NW_DST_MASK wildcard bit counts to
  * and from IP bitmasks. */
 ovs_be32 ofputil_wcbits_to_netmask(int wcbits);
@@ -57,6 +123,8 @@ void *put_openflow(size_t openflow_len, uint8_t type, struct ofpbuf *);
 void *put_openflow_xid(size_t openflow_len, uint8_t type, ovs_be32 xid,
                        struct ofpbuf *);
 void update_openflow_length(struct ofpbuf *);
+const void *ofputil_stats_body(const struct ofp_header *);
+
 struct ofpbuf *make_flow_mod(uint16_t command, const struct cls_rule *,
                              size_t actions_len);
 struct ofpbuf *make_add_flow(const struct cls_rule *, uint32_t buffer_id,
@@ -78,10 +146,6 @@ struct ofpbuf *make_unbuffered_packet_out(const struct ofpbuf *packet,
                                           uint16_t in_port, uint16_t out_port);
 struct ofpbuf *make_echo_request(void);
 struct ofpbuf *make_echo_reply(const struct ofp_header *rq);
-int check_ofp_message(const struct ofp_header *, uint8_t type, size_t size);
-int check_ofp_message_array(const struct ofp_header *, uint8_t type,
-                            size_t size, size_t array_elt_size,
-                            size_t *n_array_elts);
 
 struct flow_stats_iterator {
     const uint8_t *pos, *end;
diff --git a/ofproto/ofproto.c b/ofproto/ofproto.c
index b635ebe..329aafd 100644
--- a/ofproto/ofproto.c
+++ b/ofproto/ofproto.c
@@ -2450,15 +2450,14 @@ hton_ofp_phy_port(struct ofp_phy_port *opp)
 }
 
 static int
-handle_echo_request(struct ofconn *ofconn, struct ofp_header *oh)
+handle_echo_request(struct ofconn *ofconn, const struct ofp_header *oh)
 {
-    struct ofp_header *rq = oh;
-    queue_tx(make_echo_reply(rq), ofconn, ofconn->reply_counter);
+    queue_tx(make_echo_reply(oh), ofconn, ofconn->reply_counter);
     return 0;
 }
 
 static int
-handle_features_request(struct ofconn *ofconn, struct ofp_header *oh)
+handle_features_request(struct ofconn *ofconn, const struct ofp_header *oh)
 {
     struct ofp_switch_features *osf;
     struct ofpbuf *buf;
@@ -2492,7 +2491,7 @@ handle_features_request(struct ofconn *ofconn, struct ofp_header *oh)
 }
 
 static int
-handle_get_config_request(struct ofconn *ofconn, struct ofp_header *oh)
+handle_get_config_request(struct ofconn *ofconn, const struct ofp_header *oh)
 {
     struct ofpbuf *buf;
     struct ofp_switch_config *osc;
@@ -2513,16 +2512,9 @@ handle_get_config_request(struct ofconn *ofconn, struct ofp_header *oh)
 }
 
 static int
-handle_set_config(struct ofconn *ofconn, struct ofp_switch_config *osc)
+handle_set_config(struct ofconn *ofconn, const struct ofp_switch_config *osc)
 {
-    uint16_t flags;
-    int error;
-
-    error = check_ofp_message(&osc->header, OFPT_SET_CONFIG, sizeof *osc);
-    if (error) {
-        return error;
-    }
-    flags = ntohs(osc->flags);
+    uint16_t flags = ntohs(osc->flags);
 
     if (ofconn->type == OFCONN_PRIMARY && ofconn->role != NX_ROLE_SLAVE) {
         switch (flags & OFPC_FRAG_MASK) {
@@ -3030,7 +3022,7 @@ reject_slave_controller(struct ofconn *ofconn, const const char *msg_type)
 }
 
 static int
-handle_packet_out(struct ofconn *ofconn, struct ofp_header *oh)
+handle_packet_out(struct ofconn *ofconn, const struct ofp_header *oh)
 {
     struct ofproto *p = ofconn->ofproto;
     struct ofp_packet_out *opo;
@@ -3051,7 +3043,7 @@ handle_packet_out(struct ofconn *ofconn, struct ofp_header *oh)
     }
 
     /* Get ofp_packet_out. */
-    request.data = oh;
+    request.data = (void *) oh;
     request.size = ntohs(oh->length);
     opo = ofpbuf_try_pull(&request, offsetof(struct ofp_packet_out, actions));
     if (!opo) {
@@ -3125,10 +3117,10 @@ update_port_config(struct ofproto *p, struct ofport *port,
 }
 
 static int
-handle_port_mod(struct ofconn *ofconn, struct ofp_header *oh)
+handle_port_mod(struct ofconn *ofconn, const struct ofp_header *oh)
 {
     struct ofproto *p = ofconn->ofproto;
-    const struct ofp_port_mod *opm;
+    const struct ofp_port_mod *opm = (const struct ofp_port_mod *) oh;
     struct ofport *port;
     int error;
 
@@ -3136,11 +3128,6 @@ handle_port_mod(struct ofconn *ofconn, struct ofp_header *oh)
     if (error) {
         return error;
     }
-    error = check_ofp_message(oh, OFPT_PORT_MOD, sizeof *opm);
-    if (error) {
-        return error;
-    }
-    opm = (struct ofp_port_mod *) oh;
 
     port = get_port(p, ofp_port_to_odp_port(ntohs(opm->port_no)));
     if (!port) {
@@ -3170,9 +3157,11 @@ make_ofp_stats_reply(ovs_be32 xid, ovs_be16 type, size_t body_len)
 }
 
 static struct ofpbuf *
-start_ofp_stats_reply(const struct ofp_stats_request *request, size_t body_len)
+start_ofp_stats_reply(const struct ofp_header *request, size_t body_len)
 {
-    return make_ofp_stats_reply(request->header.xid, request->type, body_len);
+    const struct ofp_stats_request *osr
+        = (const struct ofp_stats_request *) request;
+    return make_ofp_stats_reply(osr->header.xid, osr->type, body_len);
 }
 
 static void *
@@ -3228,7 +3217,7 @@ append_nxstats_reply(size_t nbytes, struct ofconn *ofconn,
 
 static int
 handle_desc_stats_request(struct ofconn *ofconn,
-                          struct ofp_stats_request *request)
+                          const struct ofp_header *request)
 {
     struct ofproto *p = ofconn->ofproto;
     struct ofp_desc_stats *ods;
@@ -3249,7 +3238,7 @@ handle_desc_stats_request(struct ofconn *ofconn,
 
 static int
 handle_table_stats_request(struct ofconn *ofconn,
-                           struct ofp_stats_request *request)
+                           const struct ofp_header *request)
 {
     struct ofproto *p = ofconn->ofproto;
     struct ofp_table_stats *ots;
@@ -3302,21 +3291,15 @@ append_port_stat(struct ofport *port, struct ofconn *ofconn,
 }
 
 static int
-handle_port_stats_request(struct ofconn *ofconn, struct ofp_stats_request *osr,
-                          size_t arg_size)
+handle_port_stats_request(struct ofconn *ofconn, const struct ofp_header *oh)
 {
     struct ofproto *p = ofconn->ofproto;
-    struct ofp_port_stats_request *psr;
+    const struct ofp_port_stats_request *psr = ofputil_stats_body(oh);
     struct ofp_port_stats *ops;
     struct ofpbuf *msg;
     struct ofport *port;
 
-    if (arg_size != sizeof *psr) {
-        return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_LEN);
-    }
-    psr = (struct ofp_port_stats_request *) osr->body;
-
-    msg = start_ofp_stats_reply(osr, sizeof *ops * 16);
+    msg = start_ofp_stats_reply(oh, sizeof *ops * 16);
     if (psr->port_no != htons(OFPP_NONE)) {
         port = get_port(p, ofp_port_to_odp_port(ntohs(psr->port_no)));
         if (port) {
@@ -3431,19 +3414,13 @@ is_valid_table(uint8_t table_id)
 }
 
 static int
-handle_flow_stats_request(struct ofconn *ofconn,
-                          const struct ofp_stats_request *osr, size_t arg_size)
+handle_flow_stats_request(struct ofconn *ofconn, const struct ofp_header *oh)
 {
-    struct ofp_flow_stats_request *fsr;
+    const struct ofp_flow_stats_request *fsr = ofputil_stats_body(oh);
     struct ofpbuf *reply;
 
-    if (arg_size != sizeof *fsr) {
-        return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_LEN);
-    }
-    fsr = (struct ofp_flow_stats_request *) osr->body;
-
     COVERAGE_INC(ofproto_flows_req);
-    reply = start_ofp_stats_reply(osr, 1024);
+    reply = start_ofp_stats_reply(oh, 1024);
     if (is_valid_table(fsr->table_id)) {
         struct cls_cursor cursor;
         struct cls_rule target;
@@ -3501,22 +3478,29 @@ put_nx_flow_stats(struct ofconn *ofconn, struct rule *rule,
 }
 
 static int
-handle_nxst_flow(struct ofconn *ofconn, struct ofpbuf *b)
+handle_nxst_flow(struct ofconn *ofconn, const struct ofp_header *oh)
 {
     struct nx_flow_stats_request *nfsr;
     struct cls_rule target;
     struct ofpbuf *reply;
+    struct ofpbuf b;
     int error;
 
+    b.data = (void *) oh;
+    b.size = ntohs(oh->length);
+
     /* Dissect the message. */
-    nfsr = ofpbuf_try_pull(b, sizeof *nfsr);
+    nfsr = ofpbuf_try_pull(&b, sizeof *nfsr);
     if (!nfsr) {
         return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_LEN);
     }
-    error = nx_pull_match(b, ntohs(nfsr->match_len), 0, &target);
+    error = nx_pull_match(&b, ntohs(nfsr->match_len), 0, &target);
     if (error) {
         return error;
     }
+    if (b.size) {
+        return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_LEN);
+    }
 
     COVERAGE_INC(ofproto_flows_req);
     reply = start_nxstats_reply(&nfsr->nsm, 1024);
@@ -3610,23 +3594,17 @@ query_aggregate_stats(struct ofproto *ofproto, struct cls_rule *target,
 
 static int
 handle_aggregate_stats_request(struct ofconn *ofconn,
-                               const struct ofp_stats_request *osr,
-                               size_t arg_size)
+                               const struct ofp_header *oh)
 {
-    struct ofp_aggregate_stats_request *request;
+    const struct ofp_aggregate_stats_request *request = ofputil_stats_body(oh);
     struct ofp_aggregate_stats_reply *reply;
     struct cls_rule target;
     struct ofpbuf *msg;
 
-    if (arg_size != sizeof *request) {
-        return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_LEN);
-    }
-    request = (struct ofp_aggregate_stats_request *) osr->body;
-
     ofputil_cls_rule_from_match(&request->match, 0, NXFF_OPENFLOW10, 0,
                                 &target);
 
-    msg = start_ofp_stats_reply(osr, sizeof *reply);
+    msg = start_ofp_stats_reply(oh, sizeof *reply);
     reply = append_ofp_stats_reply(sizeof *reply, ofconn, &msg);
     query_aggregate_stats(ofconn->ofproto, &target, request->out_port,
                           request->table_id, reply);
@@ -3635,23 +3613,30 @@ handle_aggregate_stats_request(struct ofconn *ofconn,
 }
 
 static int
-handle_nxst_aggregate(struct ofconn *ofconn, struct ofpbuf *b)
+handle_nxst_aggregate(struct ofconn *ofconn, const struct ofp_header *oh)
 {
     struct nx_aggregate_stats_request *request;
     struct ofp_aggregate_stats_reply *reply;
     struct cls_rule target;
+    struct ofpbuf b;
     struct ofpbuf *buf;
     int error;
 
+    b.data = (void *) oh;
+    b.size = ntohs(oh->length);
+
     /* Dissect the message. */
-    request = ofpbuf_try_pull(b, sizeof *request);
+    request = ofpbuf_try_pull(&b, sizeof *request);
     if (!request) {
         return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_LEN);
     }
-    error = nx_pull_match(b, ntohs(request->match_len), 0, &target);
+    error = nx_pull_match(&b, ntohs(request->match_len), 0, &target);
     if (error) {
         return error;
     }
+    if (b.size) {
+        return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_LEN);
+    }
 
     /* Reply. */
     COVERAGE_INC(ofproto_flows_req);
@@ -3713,26 +3698,24 @@ handle_queue_stats_for_port(struct ofport *port, uint32_t queue_id,
 }
 
 static int
-handle_queue_stats_request(struct ofconn *ofconn,
-                           const struct ofp_stats_request *osr,
-                           size_t arg_size)
+handle_queue_stats_request(struct ofconn *ofconn, const struct ofp_header *oh)
 {
     struct ofproto *ofproto = ofconn->ofproto;
-    struct ofp_queue_stats_request *qsr;
+    const struct ofp_queue_stats_request *qsr;
     struct queue_stats_cbdata cbdata;
     struct ofport *port;
     unsigned int port_no;
     uint32_t queue_id;
 
-    if (arg_size != sizeof *qsr) {
+    qsr = ofputil_stats_body(oh);
+    if (!qsr) {
         return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_LEN);
     }
-    qsr = (struct ofp_queue_stats_request *) osr->body;
 
     COVERAGE_INC(ofproto_queue_req);
 
     cbdata.ofconn = ofconn;
-    cbdata.msg = start_ofp_stats_reply(osr, 128);
+    cbdata.msg = start_ofp_stats_reply(oh, 128);
 
     port_no = ntohs(qsr->port_no);
     queue_id = ntohl(qsr->queue_id);
@@ -3754,85 +3737,6 @@ handle_queue_stats_request(struct ofconn *ofconn,
     return 0;
 }
 
-static int
-handle_vendor_stats_request(struct ofconn *ofconn,
-                            struct ofp_stats_request *osr, size_t arg_size)
-{
-    struct nicira_stats_msg *nsm;
-    struct ofpbuf b;
-    ovs_be32 vendor;
-
-    if (arg_size < 4) {
-        VLOG_WARN_RL(&rl, "truncated vendor stats request body");
-        return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_LEN);
-    }
-
-    memcpy(&vendor, osr->body, sizeof vendor);
-    if (vendor != htonl(NX_VENDOR_ID)) {
-        return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_VENDOR);
-    }
-
-    if (ntohs(osr->header.length) < sizeof(struct nicira_stats_msg)) {
-        VLOG_WARN_RL(&rl, "truncated Nicira stats request");
-        return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_LEN);
-    }
-
-    nsm = (struct nicira_stats_msg *) osr;
-    b.data = nsm;
-    b.size = ntohs(nsm->header.length);
-    switch (ntohl(nsm->subtype)) {
-    case NXST_FLOW:
-        return handle_nxst_flow(ofconn, &b);
-
-    case NXST_AGGREGATE:
-        return handle_nxst_aggregate(ofconn, &b);
-
-    default:
-        return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_SUBTYPE);
-    }
-}
-
-static int
-handle_stats_request(struct ofconn *ofconn, struct ofp_header *oh)
-{
-    struct ofp_stats_request *osr;
-    size_t arg_size;
-    int error;
-
-    error = check_ofp_message_array(oh, OFPT_STATS_REQUEST, sizeof *osr,
-                                    1, &arg_size);
-    if (error) {
-        return error;
-    }
-    osr = (struct ofp_stats_request *) oh;
-
-    switch (ntohs(osr->type)) {
-    case OFPST_DESC:
-        return handle_desc_stats_request(ofconn, osr);
-
-    case OFPST_FLOW:
-        return handle_flow_stats_request(ofconn, osr, arg_size);
-
-    case OFPST_AGGREGATE:
-        return handle_aggregate_stats_request(ofconn, osr, arg_size);
-
-    case OFPST_TABLE:
-        return handle_table_stats_request(ofconn, osr);
-
-    case OFPST_PORT:
-        return handle_port_stats_request(ofconn, osr, arg_size);
-
-    case OFPST_QUEUE:
-        return handle_queue_stats_request(ofconn, osr, arg_size);
-
-    case OFPST_VENDOR:
-        return handle_vendor_stats_request(ofconn, osr, arg_size);
-
-    default:
-        return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_STAT);
-    }
-}
-
 static long long int
 msec_from_nsec(uint64_t sec, uint32_t nsec)
 {
@@ -4142,7 +4046,7 @@ flow_mod_core(struct ofconn *ofconn, struct flow_mod *fm)
 }
 
 static int
-handle_ofpt_flow_mod(struct ofconn *ofconn, struct ofp_header *oh)
+handle_ofpt_flow_mod(struct ofconn *ofconn, const struct ofp_header *oh)
 {
     struct ofp_match orig_match;
     struct ofp_flow_mod *ofm;
@@ -4150,7 +4054,7 @@ handle_ofpt_flow_mod(struct ofconn *ofconn, struct ofp_header *oh)
     struct ofpbuf b;
     int error;
 
-    b.data = oh;
+    b.data = (void *) oh;
     b.size = ntohs(oh->length);
 
     /* Dissect the message. */
@@ -4198,14 +4102,14 @@ handle_ofpt_flow_mod(struct ofconn *ofconn, struct ofp_header *oh)
 }
 
 static int
-handle_nxt_flow_mod(struct ofconn *ofconn, struct ofp_header *oh)
+handle_nxt_flow_mod(struct ofconn *ofconn, const struct ofp_header *oh)
 {
     struct nx_flow_mod *nfm;
     struct flow_mod fm;
     struct ofpbuf b;
     int error;
 
-    b.data = oh;
+    b.data = (void *) oh;
     b.size = ntohs(oh->length);
 
     /* Dissect the message. */
@@ -4237,34 +4141,23 @@ handle_nxt_flow_mod(struct ofconn *ofconn, struct ofp_header *oh)
 }
 
 static int
-handle_tun_id_from_cookie(struct ofconn *ofconn, struct nxt_tun_id_cookie *msg)
+handle_tun_id_from_cookie(struct ofconn *ofconn, const struct ofp_header *oh)
 {
-    int error;
-
-    error = check_ofp_message(&msg->header, OFPT_VENDOR, sizeof *msg);
-    if (error) {
-        return error;
-    }
+    const struct nxt_tun_id_cookie *msg
+        = (const struct nxt_tun_id_cookie *) oh;
 
     ofconn->flow_format = msg->set ? NXFF_TUN_ID_FROM_COOKIE : NXFF_OPENFLOW10;
     return 0;
 }
 
 static int
-handle_role_request(struct ofconn *ofconn, struct nicira_header *msg)
+handle_role_request(struct ofconn *ofconn, const struct ofp_header *oh)
 {
-    struct nx_role_request *nrr;
+    struct nx_role_request *nrr = (struct nx_role_request *) oh;
     struct nx_role_request *reply;
     struct ofpbuf *buf;
     uint32_t role;
 
-    if (ntohs(msg->header.length) != sizeof *nrr) {
-        VLOG_WARN_RL(&rl, "received role request of length %u (expected %zu)",
-                     ntohs(msg->header.length), sizeof *nrr);
-        return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_LEN);
-    }
-    nrr = (struct nx_role_request *) msg;
-
     if (ofconn->type != OFCONN_PRIMARY) {
         VLOG_WARN_RL(&rl, "ignoring role request on non-controller "
                      "connection");
@@ -4291,8 +4184,7 @@ handle_role_request(struct ofconn *ofconn, struct nicira_header *msg)
     }
     ofconn->role = role;
 
-    reply = make_nxmsg_xid(sizeof *reply, NXT_ROLE_REPLY, msg->header.xid,
-                           &buf);
+    reply = make_nxmsg_xid(sizeof *reply, NXT_ROLE_REPLY, oh->xid, &buf);
     reply->role = htonl(role);
     queue_tx(buf, ofconn, ofconn->reply_counter);
 
@@ -4300,16 +4192,11 @@ handle_role_request(struct ofconn *ofconn, struct nicira_header *msg)
 }
 
 static int
-handle_nxt_set_flow_format(struct ofconn *ofconn,
-                           struct nxt_set_flow_format *msg)
+handle_nxt_set_flow_format(struct ofconn *ofconn, const struct ofp_header *oh)
 {
+    const struct nxt_set_flow_format *msg
+        = (const struct nxt_set_flow_format *) oh;
     uint32_t format;
-    int error;
-
-    error = check_ofp_message(&msg->header, OFPT_VENDOR, sizeof *msg);
-    if (error) {
-        return error;
-    }
 
     format = ntohl(msg->format);
     if (format == NXFF_OPENFLOW10
@@ -4323,52 +4210,7 @@ handle_nxt_set_flow_format(struct ofconn *ofconn,
 }
 
 static int
-handle_vendor(struct ofconn *ofconn, void *msg)
-{
-    struct ofproto *p = ofconn->ofproto;
-    struct ofp_vendor_header *ovh = msg;
-    struct nicira_header *nh;
-
-    if (ntohs(ovh->header.length) < sizeof(struct ofp_vendor_header)) {
-        VLOG_WARN_RL(&rl, "received vendor message of length %u "
-                          "(expected at least %zu)",
-                   ntohs(ovh->header.length), sizeof(struct ofp_vendor_header));
-        return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_LEN);
-    }
-    if (ovh->vendor != htonl(NX_VENDOR_ID)) {
-        return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_VENDOR);
-    }
-    if (ntohs(ovh->header.length) < sizeof(struct nicira_header)) {
-        VLOG_WARN_RL(&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 = msg;
-    switch (ntohl(nh->subtype)) {
-    case NXT_STATUS_REQUEST:
-        return switch_status_handle_request(p->switch_status, ofconn->rconn,
-                                            msg);
-
-    case NXT_TUN_ID_FROM_COOKIE:
-        return handle_tun_id_from_cookie(ofconn, msg);
-
-    case NXT_ROLE_REQUEST:
-        return handle_role_request(ofconn, msg);
-
-    case NXT_SET_FLOW_FORMAT:
-        return handle_nxt_set_flow_format(ofconn, msg);
-
-    case NXT_FLOW_MOD:
-        return handle_nxt_flow_mod(ofconn, &ovh->header);
-    }
-
-    return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_SUBTYPE);
-}
-
-static int
-handle_barrier_request(struct ofconn *ofconn, struct ofp_header *oh)
+handle_barrier_request(struct ofconn *ofconn, const struct ofp_header *oh)
 {
     struct ofp_header *ob;
     struct ofpbuf *buf;
@@ -4380,71 +4222,135 @@ handle_barrier_request(struct ofconn *ofconn, struct ofp_header *oh)
     return 0;
 }
 
-static void
-handle_openflow(struct ofconn *ofconn, struct ofpbuf *ofp_msg)
+static int
+handle_openflow__(struct ofconn *ofconn, const struct ofpbuf *msg)
 {
-    struct ofp_header *oh = ofp_msg->data;
+    const struct ofp_header *oh = msg->data;
+    const struct ofputil_msg_type *type;
     int error;
 
-    COVERAGE_INC(ofproto_recv_openflow);
-    switch (oh->type) {
-    case OFPT_ECHO_REQUEST:
-        error = handle_echo_request(ofconn, oh);
-        break;
+    error = ofputil_decode_msg_type(oh, &type);
+    if (error) {
+        return error;
+    }
 
-    case OFPT_ECHO_REPLY:
-        error = 0;
-        break;
+    switch (ofputil_msg_type_code(type)) {
+        /* OpenFlow requests. */
+    case OFPUTIL_OFPT_ECHO_REQUEST:
+        return handle_echo_request(ofconn, oh);
 
-    case OFPT_FEATURES_REQUEST:
-        error = handle_features_request(ofconn, oh);
-        break;
+    case OFPUTIL_OFPT_FEATURES_REQUEST:
+        return handle_features_request(ofconn, oh);
 
-    case OFPT_GET_CONFIG_REQUEST:
-        error = handle_get_config_request(ofconn, oh);
-        break;
+    case OFPUTIL_OFPT_GET_CONFIG_REQUEST:
+        return handle_get_config_request(ofconn, oh);
 
-    case OFPT_SET_CONFIG:
-        error = handle_set_config(ofconn, ofp_msg->data);
-        break;
+    case OFPUTIL_OFPT_SET_CONFIG:
+        return handle_set_config(ofconn, msg->data);
 
-    case OFPT_PACKET_OUT:
-        error = handle_packet_out(ofconn, ofp_msg->data);
-        break;
+    case OFPUTIL_OFPT_PACKET_OUT:
+        return handle_packet_out(ofconn, oh);
 
-    case OFPT_PORT_MOD:
-        error = handle_port_mod(ofconn, oh);
-        break;
-
-    case OFPT_FLOW_MOD:
-        error = handle_ofpt_flow_mod(ofconn, ofp_msg->data);
-        break;
+    case OFPUTIL_OFPT_PORT_MOD:
+        return handle_port_mod(ofconn, oh);
 
-    case OFPT_STATS_REQUEST:
-        error = handle_stats_request(ofconn, oh);
-        break;
+    case OFPUTIL_OFPT_FLOW_MOD:
+        return handle_ofpt_flow_mod(ofconn, oh);
 
-    case OFPT_VENDOR:
-        error = handle_vendor(ofconn, ofp_msg->data);
-        break;
+    case OFPUTIL_OFPT_BARRIER_REQUEST:
+        return handle_barrier_request(ofconn, oh);
 
-    case OFPT_BARRIER_REQUEST:
-        error = handle_barrier_request(ofconn, oh);
-        break;
+        /* OpenFlow replies. */
+    case OFPUTIL_OFPT_ECHO_REPLY:
+        return 0;
 
+        /* Nicira extension requests. */
+    case OFPUTIL_NXT_STATUS_REQUEST:
+        return switch_status_handle_request(
+            ofconn->ofproto->switch_status, ofconn->rconn, oh);
+
+    case OFPUTIL_NXT_TUN_ID_FROM_COOKIE:
+        return handle_tun_id_from_cookie(ofconn, oh);
+
+    case OFPUTIL_NXT_ROLE_REQUEST:
+        return handle_role_request(ofconn, oh);
+
+    case OFPUTIL_NXT_SET_FLOW_FORMAT:
+        return handle_nxt_set_flow_format(ofconn, oh);
+
+    case OFPUTIL_NXT_FLOW_MOD:
+        return handle_nxt_flow_mod(ofconn, oh);
+
+        /* OpenFlow statistics requests. */
+    case OFPUTIL_OFPST_DESC_REQUEST:
+        return handle_desc_stats_request(ofconn, oh);
+
+    case OFPUTIL_OFPST_FLOW_REQUEST:
+        return handle_flow_stats_request(ofconn, oh);
+
+    case OFPUTIL_OFPST_AGGREGATE_REQUEST:
+        return handle_aggregate_stats_request(ofconn, oh);
+
+    case OFPUTIL_OFPST_TABLE_REQUEST:
+        return handle_table_stats_request(ofconn, oh);
+
+    case OFPUTIL_OFPST_PORT_REQUEST:
+        return handle_port_stats_request(ofconn, oh);
+
+    case OFPUTIL_OFPST_QUEUE_REQUEST:
+        return handle_queue_stats_request(ofconn, oh);
+
+        /* Nicira extension statistics requests. */
+    case OFPUTIL_NXST_FLOW_REQUEST:
+        return handle_nxst_flow(ofconn, oh);
+
+    case OFPUTIL_NXST_AGGREGATE_REQUEST:
+        return handle_nxst_aggregate(ofconn, oh);
+
+    case OFPUTIL_INVALID:
+    case OFPUTIL_OFPT_HELLO:
+    case OFPUTIL_OFPT_ERROR:
+    case OFPUTIL_OFPT_FEATURES_REPLY:
+    case OFPUTIL_OFPT_GET_CONFIG_REPLY:
+    case OFPUTIL_OFPT_PACKET_IN:
+    case OFPUTIL_OFPT_FLOW_REMOVED:
+    case OFPUTIL_OFPT_PORT_STATUS:
+    case OFPUTIL_OFPT_BARRIER_REPLY:
+    case OFPUTIL_OFPT_QUEUE_GET_CONFIG_REQUEST:
+    case OFPUTIL_OFPT_QUEUE_GET_CONFIG_REPLY:
+    case OFPUTIL_OFPST_DESC_REPLY:
+    case OFPUTIL_OFPST_FLOW_REPLY:
+    case OFPUTIL_OFPST_QUEUE_REPLY:
+    case OFPUTIL_OFPST_PORT_REPLY:
+    case OFPUTIL_OFPST_TABLE_REPLY:
+    case OFPUTIL_OFPST_AGGREGATE_REPLY:
+    case OFPUTIL_NXT_STATUS_REPLY:
+    case OFPUTIL_NXT_ROLE_REPLY:
+    case OFPUTIL_NXT_FLOW_REMOVED:
+    case OFPUTIL_NXST_FLOW_REPLY:
+    case OFPUTIL_NXST_AGGREGATE_REPLY:
     default:
         if (VLOG_IS_WARN_ENABLED()) {
             char *s = ofp_to_string(oh, ntohs(oh->length), 2);
             VLOG_DBG_RL(&rl, "OpenFlow message ignored: %s", s);
             free(s);
         }
-        error = ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_TYPE);
-        break;
+        if (oh->type == OFPT_STATS_REQUEST || oh->type == OFPT_STATS_REPLY) {
+            return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_STAT);
+        } else {
+            return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_TYPE);
+        }
     }
+}
 
+static void
+handle_openflow(struct ofconn *ofconn, struct ofpbuf *ofp_msg)
+{
+    int error = handle_openflow__(ofconn, ofp_msg);
     if (error) {
         send_error_oh(ofconn, ofp_msg->data, error);
     }
+    COVERAGE_INC(ofproto_recv_openflow);
 }
 
 static void
diff --git a/ofproto/status.c b/ofproto/status.c
index b5bc33a..67cb278 100644
--- a/ofproto/status.c
+++ b/ofproto/status.c
@@ -59,8 +59,9 @@ struct status_reply {
 
 int
 switch_status_handle_request(struct switch_status *ss, struct rconn *rconn,
-                             struct nicira_header *request)
+                             const struct ofp_header *oh)
 {
+    const struct nicira_header *request = (const struct nicira_header *) oh;
     struct status_category *c;
     struct nicira_header *reply;
     struct status_reply sr;
diff --git a/ofproto/status.h b/ofproto/status.h
index 1186fa5..b9db836 100644
--- a/ofproto/status.h
+++ b/ofproto/status.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010 Nicira Networks.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -19,16 +19,16 @@
 
 #include "compiler.h"
 
-struct nicira_header;
-struct rconn;
+struct ofp_header;
 struct ofproto;
+struct rconn;
 struct status_reply;
 
 struct switch_status *switch_status_create(const struct ofproto *);
 void switch_status_destroy(struct switch_status *);
 
 int switch_status_handle_request(struct switch_status *, struct rconn *,
-                                 struct nicira_header *);
+                                 const struct ofp_header *);
 
 typedef void status_cb_func(struct status_reply *, void *aux);
 struct status_category *switch_status_register(struct switch_status *,
diff --git a/tests/ofproto.at b/tests/ofproto.at
index 37981e3..613fcf0 100644
--- a/tests/ofproto.at
+++ b/tests/ofproto.at
@@ -24,11 +24,11 @@ AT_SETUP([ofproto - feature request, config request])
 OFPROTO_START
 AT_CHECK([ovs-ofctl -vANY:ANY:WARN show br0], [0], [stdout])
 AT_CHECK([[sed 's/ (xid=0x[0-9a-fA-F]*)//' stdout]], [0], [dnl
-features_reply: ver:0x1, dpid:fedcba9876543210
+OFPT_FEATURES_REPLY: ver:0x1, dpid:fedcba9876543210
 n_tables:2, n_buffers:256
 features: capabilities:0x87, actions:0xfff
  LOCAL(br0): addr:aa:55:aa:55:00:00, config: 0x1, state:0x1
-get_config_reply: miss_send_len=0
+OFPT_GET_CONFIG_REPLY: miss_send_len=0
 ])
 OFPROTO_STOP
 AT_CLEANUP
@@ -46,11 +46,11 @@ do
     AT_CHECK([ovs-ofctl -vANY:ANY:WARN mod-port br0 br0 $command])
     AT_CHECK([ovs-ofctl -vANY:ANY:WARN show br0], [0], [stdout])
     AT_CHECK_UNQUOTED([[sed 's/ (xid=0x[0-9a-fA-F]*)//' stdout]], [0], [dnl
-features_reply: ver:0x1, dpid:fedcba9876543210
+OFPT_FEATURES_REPLY: ver:0x1, dpid:fedcba9876543210
 n_tables:2, n_buffers:256
 features: capabilities:0x87, actions:0xfff
  LOCAL(br0): addr:aa:55:aa:55:00:00, config: $config, state:$state
-get_config_reply: miss_send_len=0
+OFPT_GET_CONFIG_REPLY: miss_send_len=0
 ])
 done
 OFPROTO_STOP
diff --git a/tests/ovs-ofctl.at b/tests/ovs-ofctl.at
index 793e1d5..e156a31 100644
--- a/tests/ovs-ofctl.at
+++ b/tests/ovs-ofctl.at
@@ -15,15 +15,15 @@ actions=drop
 ])
 AT_CHECK([ovs-ofctl parse-flows flows.txt], [0], [stdout])
 AT_CHECK([[sed 's/ (xid=0x[0-9a-fA-F]*)//' stdout]], [0], [dnl
-flow_mod: tcp,tp_src=123, ADD: actions=FLOOD
-flow_mod: in_port=65534,dl_vlan=9,dl_src=00:0a:e4:25:6b:b0, ADD: actions=drop
-flow_mod: arp,nw_src=192.168.0.1, ADD: actions=drop_spoofed_arp,NORMAL
-flow_mod: udp,dl_vlan_pcp=7, ADD: idle:5 actions=strip_vlan,output:0
-flow_mod: tcp,nw_src=192.168.0.3,tp_dst=80, ADD: actions=set_queue:37,output:1
-flow_mod: udp,nw_src=192.168.0.3,tp_dst=53, ADD: actions=pop_queue,output:1
-flow_mod: ADD: cookie:0x123456789abcdef hard:10 pri:60000 actions=CONTROLLER:65535
-flow_mod: ADD: actions=note:41.42.43.00.00.00,note:00.01.02.03.04.05.06.07.00.00.00.00.00.00,note:00.00.00.00.00.00
-flow_mod: ADD: actions=drop
+OFPT_FLOW_MOD: tcp,tp_src=123, ADD: actions=FLOOD
+OFPT_FLOW_MOD: in_port=65534,dl_vlan=9,dl_src=00:0a:e4:25:6b:b0, ADD: actions=drop
+OFPT_FLOW_MOD: arp,nw_src=192.168.0.1, ADD: actions=drop_spoofed_arp,NORMAL
+OFPT_FLOW_MOD: udp,dl_vlan_pcp=7, ADD: idle:5 actions=strip_vlan,output:0
+OFPT_FLOW_MOD: tcp,nw_src=192.168.0.3,tp_dst=80, ADD: actions=set_queue:37,output:1
+OFPT_FLOW_MOD: udp,nw_src=192.168.0.3,tp_dst=53, ADD: actions=pop_queue,output:1
+OFPT_FLOW_MOD: ADD: cookie:0x123456789abcdef hard:10 pri:60000 actions=CONTROLLER:65535
+OFPT_FLOW_MOD: ADD: actions=note:41.42.43.00.00.00,note:00.01.02.03.04.05.06.07.00.00.00.00.00.00,note:00.00.00.00.00.00
+OFPT_FLOW_MOD: ADD: actions=drop
 ])
 AT_CLEANUP
 
-- 
1.7.1





More information about the dev mailing list