[ovs-dev] [of1.1 draft 02/10] Better abstract OpenFlow error codes.

Ben Pfaff blp at nicira.com
Thu Dec 8 04:17:21 UTC 2011


This commit switches from using the actual protocol values of error codes
internally in Open vSwitch, to using abstract values that are translated to
and from protocol values at message parsing and serialization time.  I
believe that this makes the code easier to read and to write.

This is also one step along the way toward OpenFlow 1.1 support because
OpenFlow 1.1 renumbered a bunch of error codes.
---
 build-aux/extract-ofp-errors |  243 ++++++++++++++++++--------
 include/openflow/openflow.h  |   77 ---------
 lib/.gitignore               |    2 +-
 lib/automake.mk              |   13 +-
 lib/autopath.c               |    5 +-
 lib/autopath.h               |    4 +-
 lib/bundle.c                 |   22 ++-
 lib/bundle.h                 |    5 +-
 lib/dpif.c                   |   10 +-
 lib/learn.c                  |   27 ++--
 lib/learn.h                  |    4 +-
 lib/learning-switch.c        |    1 +
 lib/multipath.c              |    7 +-
 lib/multipath.h              |    6 +-
 lib/nx-match.c               |   48 ++----
 lib/nx-match.h               |   16 +-
 lib/ofp-errors.c             |  286 +++++++++++++++++++++++++++++++
 lib/ofp-errors.h             |  389 +++++++++++++++++++++++++++++++++++++++++-
 lib/ofp-print.c              |   37 ++---
 lib/ofp-util.c               |  361 ++++++++-------------------------------
 lib/ofp-util.h               |  174 ++-----------------
 lib/vconn.c                  |   11 +-
 ofproto/connmgr.c            |   21 +--
 ofproto/connmgr.h            |    7 +-
 ofproto/ofproto-dpif.c       |   19 +-
 ofproto/ofproto-provider.h   |   49 +++---
 ofproto/ofproto.c            |  145 ++++++++--------
 ofproto/pktbuf.c             |   13 +-
 ofproto/pktbuf.h             |    8 +-
 tests/ofp-print.at           |    8 +-
 tests/ovs-ofctl.at           |   92 +++++-----
 utilities/ovs-ofctl.c        |    7 +-
 32 files changed, 1217 insertions(+), 900 deletions(-)
 create mode 100644 lib/ofp-errors.c

diff --git a/build-aux/extract-ofp-errors b/build-aux/extract-ofp-errors
index c34888f..2d7d298 100755
--- a/build-aux/extract-ofp-errors
+++ b/build-aux/extract-ofp-errors
@@ -12,6 +12,15 @@ idRe = "[a-zA-Z_][a-zA-Z_0-9]*"
 tokenRe = "#?" + idRe + "|[0-9]+|."
 inComment = False
 inDirective = False
+
+def getLine():
+    global line
+    global lineNumber
+    line = inputFile.readline()
+    lineNumber += 1
+    if line == "":
+        fatal("unexpected end of input")
+
 def getToken():
     global token
     global line
@@ -58,7 +67,7 @@ def getToken():
                 return False
 
 def fatal(msg):
-    sys.stderr.write("%s:%d: error at \"%s\": %s\n" % (fileName, lineNumber, token, msg))
+    sys.stderr.write("%s:%d: %s\n" % (fileName, lineNumber, msg))
     sys.exit(1)
 
 def skipDirective():
@@ -124,95 +133,179 @@ This program reads the header files specified on the command line and
 outputs a C source file for translating OpenFlow error codes into
 strings, for use as lib/ofp-errors.c in the Open vSwitch source tree.
 
-This program is specialized for reading include/openflow/openflow.h
-and include/openflow/nicira-ext.h.  It will not work on arbitrary
-header files without extensions.''' % {"argv0": argv0}
+This program is specialized for reading lib/ofp-errors.h.  It will not
+work on arbitrary header files without extensions.\
+''' % {"argv0": argv0}
     sys.exit(0)
 
 def extract_ofp_errors(filenames):
     error_types = {}
 
+    comments = []
+    names = []
+    domain = {}
+    reverse = {}
+    for domain_name in ("OF1.0", "OF1.1", "NX1.0", "NX1.1"):
+        domain[domain_name] = {}
+        reverse[domain_name] = {}
+
     global fileName
     for fileName in filenames:
         global inputFile
         global lineNumber
         inputFile = open(fileName)
         lineNumber = 0
-        while getToken():
-            if token in ("#ifdef", "#ifndef", "#include",
-                         "#endif", "#elif", "#else", '#define'):
-                skipDirective()
-            elif match('enum'):
-                forceId()
-                enum_tag = token
-                getToken()
-
-                forceMatch("{")
-
-                constants = []
-                while isId(token):
-                    constants.append(token)
-                    getToken()
-                    if match('='):
-                        while token != ',' and token != '}':
-                            getToken()
-                    match(',')
-
-                forceMatch('}')
-
-                if enum_tag == "ofp_error_type":
-                    error_types = {}
-                    for error_type in constants:
-                        error_types[error_type] = []
-                elif enum_tag == 'nx_vendor_code':
-                    pass
-                elif enum_tag.endswith('_code'):
-                    error_type = 'OFPET_%s' % '_'.join(enum_tag.split('_')[1:-1]).upper()
-                    if error_type not in error_types:
-                        fatal("enum %s looks like an error code enumeration but %s is unknown" % (enum_tag, error_type))
-                    error_types[error_type] += constants
-            elif token in ('struct', 'union'):
-                getToken()
-                forceId()
-                getToken()
-                forceMatch('{')
-                while not match('}'):
-                    getToken()
-            elif match('OFP_ASSERT') or match('BOOST_STATIC_ASSERT'):
-                while token != ';':
-                    getToken()
-            else:
-                fatal("parse error")
+
+        while True:
+            getLine()
+            if re.match('^enum ofperr', line):
+                break
+
+        while True:
+            getLine()
+            if line.startswith('/*') or not line or line.isspace():
+                continue
+            elif re.match('^}', line):
+                break
+
+            m = re.match('^\s+/\* ((?:.(?!\.  ))+.)\.  (.*)$', line)
+            if not m:
+                fatal("unexpected syntax between errors")
+
+            dsts, comment = m.groups()
+
+            comment.rstrip()
+            while not comment.endswith('*/'):
+                getLine()
+                if line.startswith('/*') or not line or line.isspace():
+                    fatal("unexpected syntax within error")
+                comment += ' %s' % line.lstrip('* \t').rstrip(' \t\r\n')
+            comment = comment[:-2].rstrip()
+
+            getLine()
+            m = re.match('^\s+(?:OFPERR_((?:OFP|NX)[A-Z0-9_]+))(\s*=\s*OFPERR_OFS)?,',
+                         line)
+            if not m:
+                fatal("syntax error expecting enum value")
+
+            enum = m.group(1)
+
+            comments.append(comment)
+            names.append(enum)
+
+            for dst in dsts.split(', '):
+                m = re.match(r'^([A-Z0-9.]+)\((\d+)(?:,(\d+))?\)$', dst)
+                if not m:
+                    fatal("%s: syntax error in destination" % dst)
+                targets = m.group(1)
+                type_ = int(m.group(2))
+                if m.group(3):
+                    code = int(m.group(3))
+                else:
+                    code = None
+
+                target_map = {"OF":    ("OF1.0", "OF1.1"),
+                              "OF1.0": ("OF1.0",),
+                              "OF1.1": ("OF1.1",),
+                              "NX":    ("OF1.0", "OF1.1"),
+                              "NX1.0": ("OF1.0",),
+                              "NX1.1": ("OF1.1",)}
+                if targets not in target_map:
+                    fatal("%s: unknown error domain" % target)
+                for target in target_map[targets]:
+                    if type_ not in domain[target]:
+                        domain[target][type_] = {}
+                    if code in domain[target][type_]:
+                        fatal("%s: duplicate assignment in domain" % dst)
+                    domain[target][type_][code] = enum
+                    reverse[target][enum] = (type_, code)
+
         inputFile.close()
 
-    print "/* -*- buffer-read-only: t -*- */"
-    print "#include <config.h>"
-    print '#include "ofp-errors.h"'
-    print "#include <inttypes.h>"
-    print "#include <stdio.h>"
-    for fileName in sys.argv[1:]:
-        print '#include "%s"' % fileName
-    print '#include "type-props.h"'
-
-    for error_type, constants in sorted(error_types.items()):
-        tag = 'ofp_%s_code' % re.sub('^OFPET_', '', error_type).lower()
-        print_enum(tag, constants, "static ")
-    print_enum("ofp_error_type", error_types.keys(), "")
-    print """
-const char *
-ofp_error_code_to_string(uint16_t type, uint16_t code)
-{
-    switch (type) {\
-"""
-    for error_type in error_types:
-        tag = 'ofp_%s_code' % re.sub('^OFPET_', '', error_type).lower()
-        print "    case %s:" % error_type
-        print "        return %s_to_string(code);" % tag
     print """\
+/* Generated automatically; do not modify!     -*- buffer-read-only: t -*- */
+
+#define OFPERR_N_ERRORS %d
+
+struct ofperr_domain {
+    const char *name;
+    uint8_t version;
+    enum ofperr (*decode)(uint16_t type, uint16_t code);
+    enum ofperr (*decode_type)(uint16_t type);
+    struct pair errors[OFPERR_N_ERRORS];
+};
+
+static const char *error_names[OFPERR_N_ERRORS] = {
+%s
+};
+
+static const char *error_comments[OFPERR_N_ERRORS] = {
+%s
+};\
+""" % (len(names),
+       '\n'.join('    "%s",' % name for name in names),
+       '\n'.join('    "%s",' % re.sub(r'(["\\])', r'\\\1', comment)
+                 for comment in comments))
+
+    def output_domain(map, name, description, version):
+        print """
+static enum ofperr
+%s_decode(uint16_t type, uint16_t code)
+{
+    switch ((type << 16) | code) {""" % name
+        for enum in names:
+            if enum not in map:
+                continue
+            type_, code = map[enum]
+            if code is None:
+                continue
+            print "    case (%d << 16) | %d:" % (type_, code)
+            print "        return OFPERR_%s;" % enum
+        print """\
     }
-    return NULL;
-}\
-"""
+
+    return 0;
+}
+
+static enum ofperr
+%s_decode_type(uint16_t type)
+{
+    switch (type) {""" % name
+        for enum in names:
+            if enum not in map:
+                continue
+            type_, code = map[enum]
+            if code is not None:
+                continue
+            print "    case %d:" % type_
+            print "        return OFPERR_%s;" % enum
+        print """\
+    }
+
+    return 0;
+}"""
+
+        print """
+const struct ofperr_domain %s = {
+    "%s",
+    %d,
+    %s_decode,
+    %s_decode_type,
+    {""" % (name, description, version, name, name)
+        for enum in names:
+            if enum in map:
+                type_, code = map[enum]
+                if code == None:
+                    code = -1
+            else:
+                type_ = code = -1
+            print "        { %2d, %3d }, /* %s */" % (type_, code, enum)
+        print """\
+    },
+};"""
+
+    output_domain(reverse["OF1.0"], "ofperr_of10", "OpenFlow 1.0", 0x01)
+    output_domain(reverse["OF1.1"], "ofperr_of11", "OpenFlow 1.1", 0x02)
 
 if __name__ == '__main__':
     if '--help' in sys.argv:
diff --git a/include/openflow/openflow.h b/include/openflow/openflow.h
index cee62e8..fdca109 100644
--- a/include/openflow/openflow.h
+++ b/include/openflow/openflow.h
@@ -602,83 +602,6 @@ struct ofp_flow_removed {
 };
 OFP_ASSERT(sizeof(struct ofp_flow_removed) == 88);
 
-/* Values for 'type' in ofp_error_message.  These values are immutable: they
- * will not change in future versions of the protocol (although new values may
- * be added). */
-enum ofp_error_type {
-    OFPET_HELLO_FAILED,         /* Hello protocol failed. */
-    OFPET_BAD_REQUEST,          /* Request was not understood. */
-    OFPET_BAD_ACTION,           /* Error in action description. */
-    OFPET_FLOW_MOD_FAILED,      /* Problem modifying flow entry. */
-    OFPET_PORT_MOD_FAILED,      /* OFPT_PORT_MOD failed. */
-    OFPET_QUEUE_OP_FAILED       /* Queue operation failed. */
-};
-
-/* ofp_error_msg 'code' values for OFPET_HELLO_FAILED.  'data' contains an
- * ASCII text string that may give failure details. */
-enum ofp_hello_failed_code {
-    OFPHFC_INCOMPATIBLE,        /* No compatible version. */
-    OFPHFC_EPERM                /* Permissions error. */
-};
-
-/* ofp_error_msg 'code' values for OFPET_BAD_REQUEST.  'data' contains at least
- * the first 64 bytes of the failed request. */
-enum ofp_bad_request_code {
-    OFPBRC_BAD_VERSION,         /* ofp_header.version not supported. */
-    OFPBRC_BAD_TYPE,            /* ofp_header.type not supported. */
-    OFPBRC_BAD_STAT,            /* ofp_stats_msg.type not supported. */
-    OFPBRC_BAD_VENDOR,          /* Vendor not supported (in ofp_vendor_header
-                                 * or ofp_stats_msg). */
-    OFPBRC_BAD_SUBTYPE,         /* Vendor subtype not supported. */
-    OFPBRC_EPERM,               /* Permissions error. */
-    OFPBRC_BAD_LEN,             /* Wrong request length for type. */
-    OFPBRC_BUFFER_EMPTY,        /* Specified buffer has already been used. */
-    OFPBRC_BUFFER_UNKNOWN       /* Specified buffer does not exist. */
-};
-
-/* ofp_error_msg 'code' values for OFPET_BAD_ACTION.  'data' contains at least
- * the first 64 bytes of the failed request. */
-enum ofp_bad_action_code {
-    OFPBAC_BAD_TYPE,           /* Unknown action type. */
-    OFPBAC_BAD_LEN,            /* Length problem in actions. */
-    OFPBAC_BAD_VENDOR,         /* Unknown vendor id specified. */
-    OFPBAC_BAD_VENDOR_TYPE,    /* Unknown action type for vendor id. */
-    OFPBAC_BAD_OUT_PORT,       /* Problem validating output action. */
-    OFPBAC_BAD_ARGUMENT,       /* Bad action argument. */
-    OFPBAC_EPERM,              /* Permissions error. */
-    OFPBAC_TOO_MANY,           /* Can't handle this many actions. */
-    OFPBAC_BAD_QUEUE           /* Problem validating output queue. */
-};
-
-/* ofp_error_msg 'code' values for OFPET_FLOW_MOD_FAILED.  'data' contains
- * at least the first 64 bytes of the failed request. */
-enum ofp_flow_mod_failed_code {
-    OFPFMFC_ALL_TABLES_FULL,    /* Flow not added because of full tables. */
-    OFPFMFC_OVERLAP,            /* Attempted to add overlapping flow with
-                                 * CHECK_OVERLAP flag set. */
-    OFPFMFC_EPERM,              /* Permissions error. */
-    OFPFMFC_BAD_EMERG_TIMEOUT,  /* Flow not added because of non-zero idle/hard
-                                 * timeout. */
-    OFPFMFC_BAD_COMMAND,        /* Unknown command. */
-    OFPFMFC_UNSUPPORTED         /* Unsupported action list - cannot process in
-                                   the order specified. */
-};
-
-/* ofp_error_msg 'code' values for OFPET_PORT_MOD_FAILED.  'data' contains
- * at least the first 64 bytes of the failed request. */
-enum ofp_port_mod_failed_code {
-    OFPPMFC_BAD_PORT,            /* Specified port does not exist. */
-    OFPPMFC_BAD_HW_ADDR,         /* Specified hardware address is wrong. */
-};
-
-/* ofp_error msg 'code' values for OFPET_QUEUE_OP_FAILED. 'data' contains
- * at least the first 64 bytes of the failed request */
-enum ofp_queue_op_failed_code {
-    OFPQOFC_BAD_PORT,           /* Invalid port (or port does not exist). */
-    OFPQOFC_BAD_QUEUE,          /* Queue does not exist. */
-    OFPQOFC_EPERM               /* Permissions error. */
-};
-
 /* OFPT_ERROR: Error message (datapath -> controller). */
 struct ofp_error_msg {
     struct ofp_header header;
diff --git a/lib/.gitignore b/lib/.gitignore
index c5b6cac..6cbaf30 100644
--- a/lib/.gitignore
+++ b/lib/.gitignore
@@ -3,4 +3,4 @@
 /dhparams.c
 /dirs.c
 /coverage-counters.c
-/ofp-errors.c
+/ofp-errors.inc
diff --git a/lib/automake.mk b/lib/automake.mk
index c0cc906..df27393 100644
--- a/lib/automake.mk
+++ b/lib/automake.mk
@@ -277,13 +277,12 @@ lib/dirs.c: lib/dirs.c.in Makefile
 	     > lib/dirs.c.tmp
 	mv lib/dirs.c.tmp lib/dirs.c
 
-$(srcdir)/lib/ofp-errors.c: \
-	include/openflow/openflow.h include/openflow/nicira-ext.h \
-	build-aux/extract-ofp-errors
-	cd $(srcdir)/include && \
-	$(PYTHON) ../build-aux/extract-ofp-errors \
-		openflow/openflow.h openflow/nicira-ext.h > ../lib/ofp-errors.c
-EXTRA_DIST += build-aux/extract-ofp-errors
+$(srcdir)/lib/ofp-errors.inc: \
+	lib/ofp-errors.h $(srcdir)/build-aux/extract-ofp-errors
+	$(PYTHON) $(srcdir)/build-aux/extract-ofp-errors \
+		$(srcdir)/lib/ofp-errors.h > $@.tmp && mv $@.tmp $@
+lib/ofp-errors.c: lib/ofp-errors.inc
+EXTRA_DIST += build-aux/extract-ofp-errors lib/ofp-errors.inc
 
 INSTALL_DATA_LOCAL += lib-install-data-local
 lib-install-data-local:
diff --git a/lib/autopath.c b/lib/autopath.c
index 9a39c6a..321b106 100644
--- a/lib/autopath.c
+++ b/lib/autopath.c
@@ -23,6 +23,7 @@
 
 #include "flow.h"
 #include "nx-match.h"
+#include "ofp-errors.h"
 #include "ofp-util.h"
 #include "openflow/nicira-ext.h"
 #include "vlog.h"
@@ -75,7 +76,7 @@ autopath_parse(struct nx_action_autopath *ap, const char *s_)
     free(s);
 }
 
-int
+enum ofperr
 autopath_check(const struct nx_action_autopath *ap, const struct flow *flow)
 {
     int n_bits = nxm_decode_n_bits(ap->ofs_nbits);
@@ -84,7 +85,7 @@ autopath_check(const struct nx_action_autopath *ap, const struct flow *flow)
     if (n_bits < 16) {
         VLOG_WARN("at least 16 bit destination is required for autopath "
                   "action.");
-        return ofp_mkerr(OFPET_BAD_ACTION, OFPBAC_BAD_ARGUMENT);
+        return OFPERR_OFPBAC_BAD_ARGUMENT;
     }
 
     return nxm_dst_check(ap->dst, ofs, n_bits, flow);
diff --git a/lib/autopath.h b/lib/autopath.h
index 98b02b4..19e2d07 100644
--- a/lib/autopath.h
+++ b/lib/autopath.h
@@ -18,6 +18,7 @@
 #define AUTOPATH_H 1
 
 #include <stdint.h>
+#include "ofp-errors.h"
 
 struct flow;
 struct nx_action_autopath;
@@ -29,6 +30,7 @@ struct nx_action_autopath;
 void autopath_execute(const struct nx_action_autopath *, struct flow *,
                       uint16_t ofp_port);
 void autopath_parse(struct nx_action_autopath *, const char *);
-int autopath_check(const struct nx_action_autopath *, const struct flow *);
+enum ofperr autopath_check(const struct nx_action_autopath *,
+                           const struct flow *);
 
 #endif /* autopath.h */
diff --git a/lib/bundle.c b/lib/bundle.c
index af1be63..600514f 100644
--- a/lib/bundle.c
+++ b/lib/bundle.c
@@ -24,6 +24,7 @@
 #include "multipath.h"
 #include "nx-match.h"
 #include "ofpbuf.h"
+#include "ofp-errors.h"
 #include "ofp-util.h"
 #include "openflow/nicira-ext.h"
 #include "vlog.h"
@@ -100,8 +101,8 @@ bundle_execute_load(const struct nx_action_bundle *nab, struct flow *flow,
 /* Checks that 'nab' specifies a bundle action which is supported by this
  * bundle module.  Uses the 'max_ports' parameter to validate each port using
  * ofputil_check_output_port().  Returns 0 if 'nab' is supported, otherwise an
- * OpenFlow error code (as returned by ofp_mkerr()). */
-int
+ * OFPERR_* error code. */
+enum ofperr
 bundle_check(const struct nx_action_bundle *nab, int max_ports,
              const struct flow *flow)
 {
@@ -109,7 +110,7 @@ bundle_check(const struct nx_action_bundle *nab, int max_ports,
     uint16_t n_slaves, fields, algorithm, subtype;
     uint32_t slave_type;
     size_t slaves_size, i;
-    int error;
+    enum ofperr error;
 
     subtype = ntohs(nab->subtype);
     n_slaves = ntohs(nab->n_slaves);
@@ -118,7 +119,7 @@ bundle_check(const struct nx_action_bundle *nab, int max_ports,
     slave_type = ntohl(nab->slave_type);
     slaves_size = ntohs(nab->len) - sizeof *nab;
 
-    error = ofp_mkerr(OFPET_BAD_ACTION, OFPBAC_BAD_ARGUMENT);
+    error = OFPERR_OFPBAC_BAD_ARGUMENT;
     if (!flow_hash_fields_valid(fields)) {
         VLOG_WARN_RL(&rl, "unsupported fields %"PRIu16, fields);
     } else if (n_slaves > BUNDLE_MAX_SLAVES) {
@@ -135,13 +136,13 @@ bundle_check(const struct nx_action_bundle *nab, int max_ports,
     for (i = 0; i < sizeof(nab->zero); i++) {
         if (nab->zero[i]) {
             VLOG_WARN_RL(&rl, "reserved field is nonzero");
-            error = ofp_mkerr(OFPET_BAD_ACTION, OFPBAC_BAD_ARGUMENT);
+            error = OFPERR_OFPBAC_BAD_ARGUMENT;
         }
     }
 
     if (subtype == NXAST_BUNDLE && (nab->ofs_nbits || nab->dst)) {
         VLOG_WARN_RL(&rl, "bundle action has nonzero reserved fields");
-        error = ofp_mkerr(OFPET_BAD_ACTION, OFPBAC_BAD_ARGUMENT);
+        error = OFPERR_OFPBAC_BAD_ARGUMENT;
     }
 
     if (subtype == NXAST_BUNDLE_LOAD) {
@@ -151,7 +152,7 @@ bundle_check(const struct nx_action_bundle *nab, int max_ports,
         if (n_bits < 16) {
             VLOG_WARN_RL(&rl, "bundle_load action requires at least 16 bit "
                          "destination.");
-            error = ofp_mkerr(OFPET_BAD_ACTION, OFPBAC_BAD_ARGUMENT);
+            error = OFPERR_OFPBAC_BAD_ARGUMENT;
         } else {
             error = nxm_dst_check(nab->dst, ofs, n_bits, flow) || error;
         }
@@ -162,13 +163,14 @@ bundle_check(const struct nx_action_bundle *nab, int max_ports,
                      "allocated for slaves.  %zu bytes are required for "
                      "%"PRIu16" slaves.", subtype, slaves_size,
                      n_slaves * sizeof(ovs_be16), n_slaves);
-        error = ofp_mkerr(OFPET_BAD_ACTION, OFPBAC_BAD_LEN);
+        error = OFPERR_OFPBAC_BAD_LEN;
     }
 
     for (i = 0; i < n_slaves; i++) {
         uint16_t ofp_port = bundle_get_slave(nab, i);
-        int ofputil_error = ofputil_check_output_port(ofp_port, max_ports);
+        enum ofperr ofputil_error;
 
+        ofputil_error = ofputil_check_output_port(ofp_port, max_ports);
         if (ofputil_error) {
             VLOG_WARN_RL(&rl, "invalid slave %"PRIu16, ofp_port);
             error = ofputil_error;
@@ -179,7 +181,7 @@ bundle_check(const struct nx_action_bundle *nab, int max_ports,
          * seem to be a real-world use-case for supporting it. */
         if (ofp_port == OFPP_CONTROLLER) {
             VLOG_WARN_RL(&rl, "unsupported controller slave");
-            error = ofp_mkerr(OFPET_BAD_ACTION, OFPBAC_BAD_OUT_PORT);
+            error = OFPERR_OFPBAC_BAD_OUT_PORT;
         }
     }
 
diff --git a/lib/bundle.h b/lib/bundle.h
index 12497f7..580ecf8 100644
--- a/lib/bundle.h
+++ b/lib/bundle.h
@@ -21,6 +21,7 @@
 #include <stddef.h>
 #include <stdint.h>
 
+#include "ofp-errors.h"
 #include "openflow/nicira-ext.h"
 #include "openvswitch/types.h"
 
@@ -38,8 +39,8 @@ uint16_t bundle_execute(const struct nx_action_bundle *, const struct flow *,
 void bundle_execute_load(const struct nx_action_bundle *, struct flow *,
                          bool (*slave_enabled)(uint16_t ofp_port, void *aux),
                          void *aux);
-int bundle_check(const struct nx_action_bundle *, int max_ports,
-                 const struct flow *);
+enum ofperr bundle_check(const struct nx_action_bundle *, int max_ports,
+                         const struct flow *);
 void bundle_parse(struct ofpbuf *, const char *);
 void bundle_parse_load(struct ofpbuf *b, const char *);
 void bundle_format(const struct nx_action_bundle *, struct ds *);
diff --git a/lib/dpif.c b/lib/dpif.c
index 9cbf877..700b27a 100644
--- a/lib/dpif.c
+++ b/lib/dpif.c
@@ -30,6 +30,7 @@
 #include "netdev.h"
 #include "netlink.h"
 #include "odp-util.h"
+#include "ofp-errors.h"
 #include "ofp-print.h"
 #include "ofp-util.h"
 #include "ofpbuf.h"
@@ -1201,13 +1202,12 @@ log_operation(const struct dpif *dpif, const char *operation, int error)
 {
     if (!error) {
         VLOG_DBG_RL(&dpmsg_rl, "%s: %s success", dpif_name(dpif), operation);
-    } else if (is_errno(error)) {
+    } else if (ofperr_is_valid(error)) {
         VLOG_WARN_RL(&error_rl, "%s: %s failed (%s)",
-                     dpif_name(dpif), operation, strerror(error));
+                     dpif_name(dpif), operation, ofperr_get_name(error));
     } else {
-        VLOG_WARN_RL(&error_rl, "%s: %s failed (%d/%d)",
-                     dpif_name(dpif), operation,
-                     get_ofp_err_type(error), get_ofp_err_code(error));
+        VLOG_WARN_RL(&error_rl, "%s: %s failed (%s)",
+                     dpif_name(dpif), operation, strerror(error));
     }
 }
 
diff --git a/lib/learn.c b/lib/learn.c
index 9d97cb3..241f3d1 100644
--- a/lib/learn.c
+++ b/lib/learn.c
@@ -22,6 +22,7 @@
 #include "dynamic-string.h"
 #include "meta-flow.h"
 #include "nx-match.h"
+#include "ofp-errors.h"
 #include "ofp-util.h"
 #include "ofpbuf.h"
 #include "openflow/openflow.h"
@@ -81,7 +82,7 @@ learn_min_len(uint16_t header)
     return min_len;
 }
 
-static int
+static enum ofperr
 learn_check_header(uint16_t header, size_t len)
 {
     int src_type = header & NX_LEARN_SRC_MASK;
@@ -94,12 +95,12 @@ learn_check_header(uint16_t header, size_t len)
          src_type == NX_LEARN_SRC_FIELD)) {
         /* OK. */
     } else {
-        return ofp_mkerr(OFPET_BAD_ACTION, OFPBAC_BAD_ARGUMENT);
+        return OFPERR_OFPBAC_BAD_ARGUMENT;
     }
 
     /* Check that the arguments don't overrun the end of the action. */
     if (len < learn_min_len(header)) {
-        return ofp_mkerr(OFPET_BAD_ACTION, OFPBAC_BAD_LEN);
+        return OFPERR_OFPBAC_BAD_LEN;
     }
 
     return 0;
@@ -107,7 +108,7 @@ learn_check_header(uint16_t header, size_t len)
 
 /* Checks that 'learn' (which must be at least 'sizeof *learn' bytes long) is a
  * valid action on 'flow'. */
-int
+enum ofperr
 learn_check(const struct nx_action_learn *learn, const struct flow *flow)
 {
     struct cls_rule rule;
@@ -118,7 +119,7 @@ learn_check(const struct nx_action_learn *learn, const struct flow *flow)
     if (learn->flags & ~htons(OFPFF_SEND_FLOW_REM)
         || !is_all_zeros(learn->pad, sizeof learn->pad)
         || learn->table_id == 0xff) {
-        return ofp_mkerr(OFPET_BAD_ACTION, OFPBAC_BAD_ARGUMENT);
+        return OFPERR_OFPBAC_BAD_ARGUMENT;
     }
 
     end = (char *) learn + ntohs(learn->len);
@@ -128,8 +129,8 @@ learn_check(const struct nx_action_learn *learn, const struct flow *flow)
         int src_type = header & NX_LEARN_SRC_MASK;
         int dst_type = header & NX_LEARN_DST_MASK;
 
+        enum ofperr error;
         uint64_t value;
-        int error;
 
         if (!header) {
             break;
@@ -158,7 +159,6 @@ learn_check(const struct nx_action_learn *learn, const struct flow *flow)
         if (dst_type == NX_LEARN_DST_MATCH || dst_type == NX_LEARN_DST_LOAD) {
             ovs_be32 dst_field = get_be32(&p);
             int dst_ofs = ntohs(get_be16(&p));
-            int error;
 
             error = (dst_type == NX_LEARN_DST_LOAD
                      ? nxm_dst_check(dst_field, dst_ofs, n_bits, &rule.flow)
@@ -175,7 +175,7 @@ learn_check(const struct nx_action_learn *learn, const struct flow *flow)
         }
     }
     if (!is_all_zeros(p, (char *) end - (char *) p)) {
-        return ofp_mkerr(OFPET_BAD_ACTION, OFPBAC_BAD_ARGUMENT);
+        return OFPERR_OFPBAC_BAD_ARGUMENT;
     }
 
     return 0;
@@ -412,9 +412,9 @@ learn_parse(struct ofpbuf *b, char *arg, const struct flow *flow)
 {
     char *orig = xstrdup(arg);
     char *name, *value;
+    enum ofperr error;
     size_t learn_ofs;
     size_t len;
-    int error;
 
     struct nx_action_learn *learn;
     struct cls_rule rule;
@@ -512,8 +512,7 @@ learn_parse(struct ofpbuf *b, char *arg, const struct flow *flow)
     /* In theory the above should have caught any errors, but... */
     error = learn_check(learn, flow);
     if (error) {
-        char *msg = ofputil_error_to_string(error);
-        ovs_fatal(0, "%s: %s", orig, msg);
+        ovs_fatal(0, "%s: %s", orig, ofperr_to_string(error));
     }
     free(orig);
 }
@@ -566,7 +565,7 @@ learn_format(const struct nx_action_learn *learn, struct ds *s)
         int dst_ofs;
         const struct mf_field *dst_field;
 
-        int error;
+        enum ofperr error;
         int i;
 
         if (!header) {
@@ -574,11 +573,11 @@ learn_format(const struct nx_action_learn *learn, struct ds *s)
         }
 
         error = learn_check_header(header, (char *) end - (char *) p);
-        if (error == ofp_mkerr(OFPET_BAD_ACTION, OFPBAC_BAD_ARGUMENT)) {
+        if (error == OFPERR_OFPBAC_BAD_ARGUMENT) {
             ds_put_format(s, ",***bad flow_mod_spec header %"PRIx16"***)",
                           header);
             return;
-        } else if (error == ofp_mkerr(OFPET_BAD_ACTION, OFPBAC_BAD_LEN)) {
+        } else if (error == OFPERR_OFPBAC_BAD_LEN) {
             ds_put_format(s, ",***flow_mod_spec at offset %td is %u bytes "
                           "long but only %td bytes are left***)",
                           (char *) p - (char *) (learn + 1) - 2,
diff --git a/lib/learn.h b/lib/learn.h
index 19a9089..b83bee2 100644
--- a/lib/learn.h
+++ b/lib/learn.h
@@ -17,6 +17,8 @@
 #ifndef LEARN_H
 #define LEARN_H 1
 
+#include "ofp-errors.h"
+
 struct ds;
 struct flow;
 struct ofpbuf;
@@ -28,7 +30,7 @@ struct nx_action_learn;
  * See include/openflow/nicira-ext.h for NXAST_LEARN specification.
  */
 
-int learn_check(const struct nx_action_learn *, const struct flow *);
+enum ofperr learn_check(const struct nx_action_learn *, const struct flow *);
 void learn_execute(const struct nx_action_learn *, const struct flow *,
                    struct ofputil_flow_mod *);
 
diff --git a/lib/learning-switch.c b/lib/learning-switch.c
index ecc5509..c47fcb6 100644
--- a/lib/learning-switch.c
+++ b/lib/learning-switch.c
@@ -29,6 +29,7 @@
 #include "hmap.h"
 #include "mac-learning.h"
 #include "ofpbuf.h"
+#include "ofp-errors.h"
 #include "ofp-parse.h"
 #include "ofp-print.h"
 #include "ofp-util.h"
diff --git a/lib/multipath.c b/lib/multipath.c
index f68dafd..80d801d 100644
--- a/lib/multipath.c
+++ b/lib/multipath.c
@@ -23,6 +23,7 @@
 #include <netinet/in.h>
 #include "dynamic-string.h"
 #include "nx-match.h"
+#include "ofp-errors.h"
 #include "ofp-util.h"
 #include "openflow/nicira-ext.h"
 #include "packets.h"
@@ -33,14 +34,14 @@ VLOG_DEFINE_THIS_MODULE(multipath);
 static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
 
 /* multipath_check(). */
-int
+enum ofperr
 multipath_check(const struct nx_action_multipath *mp, const struct flow *flow)
 {
     uint32_t n_links = ntohs(mp->max_link) + 1;
     size_t min_n_bits = log_2_floor(n_links) + 1;
     int ofs = nxm_decode_ofs(mp->ofs_nbits);
     int n_bits = nxm_decode_n_bits(mp->ofs_nbits);
-    int error;
+    enum ofperr error;
 
     error = nxm_dst_check(mp->dst, ofs, n_bits, flow);
     if (error) {
@@ -62,7 +63,7 @@ multipath_check(const struct nx_action_multipath *mp, const struct flow *flow)
         return 0;
     }
 
-    return ofp_mkerr(OFPET_BAD_ACTION, OFPBAC_BAD_ARGUMENT);
+    return OFPERR_OFPBAC_BAD_ARGUMENT;
 }
 
 /* multipath_execute(). */
diff --git a/lib/multipath.h b/lib/multipath.h
index 8ac4bfd..3c4ff45 100644
--- a/lib/multipath.h
+++ b/lib/multipath.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010 Nicira Networks.
+ * Copyright (c) 2010, 2011 Nicira Networks.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -18,6 +18,7 @@
 #define MULTIPATH_H 1
 
 #include <stdint.h>
+#include "ofp-errors.h"
 
 struct ds;
 struct flow;
@@ -29,7 +30,8 @@ struct nx_action_reg_move;
  * See include/openflow/nicira-ext.h for NXAST_MULTIPATH specification.
  */
 
-int multipath_check(const struct nx_action_multipath *, const struct flow *);
+enum ofperr multipath_check(const struct nx_action_multipath *,
+                            const struct flow *);
 void multipath_execute(const struct nx_action_multipath *, struct flow *);
 
 void multipath_parse(struct nx_action_multipath *, const char *);
diff --git a/lib/nx-match.c b/lib/nx-match.c
index 9b3c1e0..e8aee53 100644
--- a/lib/nx-match.c
+++ b/lib/nx-match.c
@@ -23,6 +23,7 @@
 #include "classifier.h"
 #include "dynamic-string.h"
 #include "meta-flow.h"
+#include "ofp-errors.h"
 #include "ofp-util.h"
 #include "ofpbuf.h"
 #include "openflow/nicira-ext.h"
@@ -36,16 +37,6 @@ VLOG_DEFINE_THIS_MODULE(nx_match);
  * peer and so there's not much point in showing a lot of them. */
 static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
 
-enum {
-    NXM_INVALID = OFP_MKERR_NICIRA(OFPET_BAD_REQUEST, NXBRC_NXM_INVALID),
-    NXM_BAD_TYPE = OFP_MKERR_NICIRA(OFPET_BAD_REQUEST, NXBRC_NXM_BAD_TYPE),
-    NXM_BAD_VALUE = OFP_MKERR_NICIRA(OFPET_BAD_REQUEST, NXBRC_NXM_BAD_VALUE),
-    NXM_BAD_MASK = OFP_MKERR_NICIRA(OFPET_BAD_REQUEST, NXBRC_NXM_BAD_MASK),
-    NXM_BAD_PREREQ = OFP_MKERR_NICIRA(OFPET_BAD_REQUEST, NXBRC_NXM_BAD_PREREQ),
-    NXM_DUP_TYPE = OFP_MKERR_NICIRA(OFPET_BAD_REQUEST, NXBRC_NXM_DUP_TYPE),
-    BAD_ARGUMENT = OFP_MKERR(OFPET_BAD_ACTION, OFPBAC_BAD_ARGUMENT)
-};
-
 /* Returns the width of the data for a field with the given 'header', in
  * bytes. */
 int
@@ -96,7 +87,7 @@ nx_entry_ok(const void *p, unsigned int match_len)
     return header;
 }
 
-int
+enum ofperr
 nx_pull_match(struct ofpbuf *b, unsigned int match_len, uint16_t priority,
               struct cls_rule *rule)
 {
@@ -108,29 +99,29 @@ nx_pull_match(struct ofpbuf *b, unsigned int match_len, uint16_t priority,
         VLOG_DBG_RL(&rl, "nx_match length %u, rounded up to a "
                     "multiple of 8, is longer than space in message (max "
                     "length %zu)", match_len, b->size);
-        return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_LEN);
+        return OFPERR_OFPBRC_BAD_LEN;
     }
 
     cls_rule_init_catchall(rule, priority);
     while ((header = nx_entry_ok(p, match_len)) != 0) {
         unsigned length = NXM_LENGTH(header);
         const struct mf_field *mf;
-        int error;
+        enum ofperr error;
 
         mf = mf_from_nxm_header(header);
         if (!mf) {
-            error = NXM_BAD_TYPE;
+            error = OFPERR_NXBRC_NXM_BAD_TYPE;
         } else if (!mf_are_prereqs_ok(mf, &rule->flow)) {
-            error = NXM_BAD_PREREQ;
+            error = OFPERR_NXBRC_NXM_BAD_PREREQ;
         } else if (!mf_is_all_wild(mf, &rule->wc)) {
-            error = NXM_DUP_TYPE;
+            error = OFPERR_NXBRC_NXM_DUP_TYPE;
         } else {
             unsigned int width = mf->n_bytes;
             union mf_value value;
 
             memcpy(&value, p + 4, width);
             if (!mf_is_value_valid(mf, &value)) {
-                error = NXM_BAD_VALUE;
+                error = OFPERR_NXBRC_NXM_BAD_VALUE;
             } else if (!NXM_HASMASK(header)) {
                 error = 0;
                 mf_set_value(mf, &value, rule);
@@ -139,7 +130,7 @@ nx_pull_match(struct ofpbuf *b, unsigned int match_len, uint16_t priority,
 
                 memcpy(&mask, p + 4 + width, width);
                 if (!mf_is_mask_valid(mf, &mask)) {
-                    error = NXM_BAD_MASK;
+                    error = OFPERR_NXBRC_NXM_BAD_MASK;
                 } else {
                     error = 0;
                     mf_set(mf, &value, &mask, rule);
@@ -148,15 +139,12 @@ nx_pull_match(struct ofpbuf *b, unsigned int match_len, uint16_t priority,
         }
 
         if (error) {
-            char *msg = ofputil_error_to_string(error);
             VLOG_DBG_RL(&rl, "bad nxm_entry %#08"PRIx32" (vendor=%"PRIu32", "
                         "field=%"PRIu32", hasmask=%"PRIu32", len=%"PRIu32"), "
                         "(%s)", header,
                         NXM_VENDOR(header), NXM_FIELD(header),
                         NXM_HASMASK(header), NXM_LENGTH(header),
-                        msg);
-            free(msg);
-
+                        ofperr_to_string(error));
             return error;
         }
 
@@ -164,7 +152,7 @@ nx_pull_match(struct ofpbuf *b, unsigned int match_len, uint16_t priority,
         match_len -= 4 + length;
     }
 
-    return match_len ? NXM_INVALID : 0;
+    return match_len ? OFPERR_NXBRC_NXM_INVALID : 0;
 }
 
 /* nx_put_match() and helpers.
@@ -918,7 +906,7 @@ nxm_check_reg_move(const struct nx_action_reg_move *action,
 
 /* Given a flow, checks that the source field represented by 'src_header'
  * in the range ['ofs', 'ofs' + 'n_bits') is valid. */
-int
+enum ofperr
 nxm_src_check(ovs_be32 src_header_, unsigned int ofs, unsigned int n_bits,
               const struct flow *flow)
 {
@@ -933,12 +921,12 @@ nxm_src_check(ovs_be32 src_header_, unsigned int ofs, unsigned int n_bits,
         return 0;
     }
 
-    return BAD_ARGUMENT;
+    return OFPERR_OFPBAC_BAD_ARGUMENT;
 }
 
 /* Given a flow, checks that the destination field represented by 'dst_header'
  * in the range ['ofs', 'ofs' + 'n_bits') is valid. */
-int
+enum ofperr
 nxm_dst_check(ovs_be32 dst_header_, unsigned int ofs, unsigned int n_bits,
               const struct flow *flow)
 {
@@ -955,16 +943,16 @@ nxm_dst_check(ovs_be32 dst_header_, unsigned int ofs, unsigned int n_bits,
         return 0;
     }
 
-    return BAD_ARGUMENT;
+    return OFPERR_OFPBAC_BAD_ARGUMENT;
 }
 
-int
+enum ofperr
 nxm_check_reg_load(const struct nx_action_reg_load *action,
                    const struct flow *flow)
 {
     unsigned int ofs = nxm_decode_ofs(action->ofs_nbits);
     unsigned int n_bits = nxm_decode_n_bits(action->ofs_nbits);
-    int error;
+    enum ofperr error;
 
     error = nxm_dst_check(action->dst, ofs, n_bits, flow);
     if (error) {
@@ -974,7 +962,7 @@ nxm_check_reg_load(const struct nx_action_reg_load *action,
     /* Reject 'action' if a bit numbered 'n_bits' or higher is set to 1 in
      * action->value. */
     if (n_bits < 64 && ntohll(action->value) >> n_bits) {
-        return BAD_ARGUMENT;
+        return OFPERR_OFPBAC_BAD_ARGUMENT;
     }
 
     return 0;
diff --git a/lib/nx-match.h b/lib/nx-match.h
index faeacd6..5ad1618 100644
--- a/lib/nx-match.h
+++ b/lib/nx-match.h
@@ -21,6 +21,7 @@
 #include <sys/types.h>
 #include <netinet/in.h>
 #include "openvswitch/types.h"
+#include "ofp-errors.h"
 
 struct cls_rule;
 struct ds;
@@ -34,8 +35,8 @@ struct nx_action_reg_move;
  * See include/openflow/nicira-ext.h for NXM specification.
  */
 
-int nx_pull_match(struct ofpbuf *, unsigned int match_len, uint16_t priority,
-                  struct cls_rule *);
+enum ofperr nx_pull_match(struct ofpbuf *, unsigned int match_len,
+                          uint16_t priority, struct cls_rule *);
 int nx_put_match(struct ofpbuf *, const struct cls_rule *);
 
 char *nx_match_to_string(const uint8_t *, unsigned int match_len);
@@ -51,11 +52,12 @@ void nxm_format_reg_move(const struct nx_action_reg_move *, struct ds *);
 void nxm_format_reg_load(const struct nx_action_reg_load *, struct ds *);
 
 int nxm_check_reg_move(const struct nx_action_reg_move *, const struct flow *);
-int nxm_check_reg_load(const struct nx_action_reg_load *, const struct flow *);
-int nxm_src_check(ovs_be32 src, unsigned int ofs, unsigned int n_bits,
-                  const struct flow *);
-int nxm_dst_check(ovs_be32 dst, unsigned int ofs, unsigned int n_bits,
-                  const struct flow *);
+enum ofperr nxm_check_reg_load(const struct nx_action_reg_load *,
+                               const struct flow *);
+enum ofperr nxm_src_check(ovs_be32 src, unsigned int ofs, unsigned int n_bits,
+                          const struct flow *);
+enum ofperr nxm_dst_check(ovs_be32 dst, unsigned int ofs, unsigned int n_bits,
+                          const struct flow *);
 
 void nxm_execute_reg_move(const struct nx_action_reg_move *, struct flow *);
 void nxm_execute_reg_load(const struct nx_action_reg_load *, struct flow *);
diff --git a/lib/ofp-errors.c b/lib/ofp-errors.c
new file mode 100644
index 0000000..785464e
--- /dev/null
+++ b/lib/ofp-errors.c
@@ -0,0 +1,286 @@
+#include <config.h>
+#include "ofp-errors.h"
+#include <errno.h>
+#include "byte-order.h"
+#include "dynamic-string.h"
+#include "ofp-util.h"
+#include "ofpbuf.h"
+#include "openflow/openflow.h"
+#include "vlog.h"
+
+VLOG_DEFINE_THIS_MODULE(ofp_errors);
+
+struct pair {
+    int type, code;
+};
+
+#include "ofp-errors.inc"
+
+/* Returns an ofperr_domain that corresponds to the OpenFlow version number
+ * 'version' (one of the possible values of struct ofp_header's 'version'
+ * member).  Returns NULL if the version isn't defined or isn't understood by
+ * OVS. */
+const struct ofperr_domain *
+ofperr_domain_from_version(uint8_t version)
+{
+    return (version == ofperr_of10.version ? &ofperr_of10
+            : version == ofperr_of11.version ? &ofperr_of11
+            : NULL);
+}
+
+/* Returns true if 'error' is a valid OFPERR_* value, false otherwise. */
+bool
+ofperr_is_valid(enum ofperr error)
+{
+    return error >= OFPERR_OFS && error < OFPERR_OFS + OFPERR_N_ERRORS;
+}
+
+/* Returns true if 'error' is a valid OFPERR_* value that designates a whole
+ * category of errors instead of a particular error, e.g. if it is an
+ * OFPERR_OFPET_* value, and false otherwise.  */
+bool
+ofperr_is_category(enum ofperr error)
+{
+    return (ofperr_is_valid(error)
+            && ofperr_of10.errors[error - OFPERR_OFS].code == -1
+            && ofperr_of11.errors[error - OFPERR_OFS].code == -1);
+}
+
+/* Returns true if 'error' is a valid OFPERR_* value that is a Nicira
+ * extension, e.g. if it is an OFPERR_NX* value, and false otherwise. */
+bool
+ofperr_is_nx_extension(enum ofperr error)
+{
+    return (ofperr_is_valid(error)
+            && (ofperr_of10.errors[error - OFPERR_OFS].code >= 0x100 ||
+                ofperr_of11.errors[error - OFPERR_OFS].code >= 0x100));
+}
+
+/* Returns true if 'error' can be encoded as an OpenFlow error message in
+ * 'domain', false otherwise.
+ *
+ * A given error may not be encodable in some domains because each OpenFlow
+ * version tends to introduce new errors and retire some old ones. */
+bool
+ofperr_is_encodable(enum ofperr error, const struct ofperr_domain *domain)
+{
+    return (ofperr_is_valid(error)
+            && domain->errors[error - OFPERR_OFS].code >= 0);
+}
+
+/* Returns the OFPERR_* value that corresponds to 'type' and 'code' within
+ * 'domain', or 0 if no such OFPERR_* value exists. */
+enum ofperr
+ofperr_decode(const struct ofperr_domain *domain, uint16_t type, uint16_t code)
+{
+    return domain->decode(type, code);
+}
+
+/* Returns the OFPERR_* value that corresponds to the category 'type' within
+ * 'domain', or 0 if no such OFPERR_* value exists. */
+enum ofperr
+ofperr_decode_type(const struct ofperr_domain *domain, uint16_t type)
+{
+    return domain->decode_type(type);
+}
+
+/* Returns the name of 'error', e.g. "OFPBRC_BAD_TYPE" if 'error' is
+ * OFPBRC_BAD_TYPE, or "<invalid>" if 'error' is not a valid OFPERR_* value.
+ *
+ * Consider ofperr_to_string() instead, if the error code might be an errno
+ * value. */
+const char *
+ofperr_get_name(enum ofperr error)
+{
+    return (ofperr_is_valid(error)
+            ? error_names[error - OFPERR_OFS]
+            : "<invalid>");
+}
+
+/* Returns an extended description name of 'error', e.g. "ofp_header.type not
+ * supported." if 'error' is OFPBRC_BAD_TYPE, or "<invalid>" if 'error' is not
+ * a valid OFPERR_* value. */
+const char *
+ofperr_get_description(enum ofperr error)
+{
+    return (ofperr_is_valid(error)
+            ? error_comments[error - OFPERR_OFS]
+            : "<invalid>");
+}
+
+static struct ofpbuf *
+ofperr_encode_msg__(enum ofperr error, const struct ofperr_domain *domain,
+                    ovs_be32 xid, const void *data, size_t data_len)
+{
+    struct ofp_error_msg *oem;
+    const struct pair *pair;
+    struct ofpbuf *buf;
+
+    if (!domain) {
+        return NULL;
+    }
+
+    if (!ofperr_is_encodable(error, domain)) {
+        static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
+
+        if (!ofperr_is_valid(error)) {
+            /* 'error' seems likely to be a system errno value. */
+            VLOG_WARN_RL(&rl, "invalid OpenFlow error code %d (%s)",
+                         error, strerror(error));
+        } else {
+            const char *s = ofperr_get_name(error);
+            if (ofperr_is_category(error)) {
+                VLOG_WARN_RL(&rl, "cannot encode error category (%s)", s);
+            } else {
+                VLOG_WARN_RL(&rl, "cannot encode %s for %s", s, domain->name);
+            }
+        }
+
+        return NULL;
+    }
+
+    pair = &domain->errors[MIN(error - OFPERR_OFS, OFPERR_N_ERRORS)];
+    if (!ofperr_is_nx_extension(error)) {
+        oem = make_openflow_xid(data_len + sizeof *oem, OFPT_ERROR, xid, &buf);
+        oem->type = htons(pair->type);
+        oem->code = htons(pair->code);
+    } else {
+        struct nx_vendor_error *nve;
+
+        oem = make_openflow_xid(data_len + sizeof *oem + sizeof *nve,
+                                OFPT_ERROR, xid, &buf);
+        oem->type = htons(NXET_VENDOR);
+        oem->code = htons(NXVC_VENDOR_ERROR);
+
+        nve = (struct nx_vendor_error *)oem->data;
+        nve->vendor = htonl(NX_VENDOR_ID);
+        nve->type = htons(pair->type);
+        nve->code = htons(pair->code);
+    }
+    oem->header.version = domain->version;
+
+    buf->size -= data_len;
+    ofpbuf_put(buf, data, data_len);
+
+    return buf;
+}
+
+/* Creates and returns an OpenFlow message of type OFPT_ERROR that conveys the
+ * given 'error'.
+ *
+ * 'oh->version' determines the OpenFlow version of the error reply.
+ * 'oh->xid' determines the xid of the error reply.
+ * The error reply will contain an initial subsequence of 'oh', up to
+ * 'oh->length' or 64 bytes, whichever is shorter.
+ *
+ * Returns NULL if 'error' is not an OpenFlow error code or if 'error' cannot
+ * be encoded as OpenFlow version 'oh->version'.
+ *
+ * This function isn't appropriate for encoding OFPET_HELLO_FAILED error
+ * messages.  Use ofperr_encode_hello() instead. */
+struct ofpbuf *
+ofperr_encode_reply(enum ofperr error, const struct ofp_header *oh)
+{
+    const struct ofperr_domain *domain;
+    uint16_t len = ntohs(oh->length);
+
+    domain = ofperr_domain_from_version(oh->version);
+    return ofperr_encode_msg__(error, domain, oh->xid, oh, MIN(len, 64));
+}
+
+/* Creates and returns an OpenFlow message of type OFPT_ERROR that conveys the
+ * given 'error', in the error domain 'domain'.  The error message will include
+ * the additional null-terminated text string 's'.
+ *
+ * If 'domain' is NULL, uses the OpenFlow 1.0 error domain.  OFPET_HELLO_FAILED
+ * error messages are supposed to be backward-compatible, so in theory this
+ * should work.
+ *
+ * Returns NULL if 'error' is not an OpenFlow error code or if 'error' cannot
+ * be encoded in 'domain'. */
+struct ofpbuf *
+ofperr_encode_hello(enum ofperr error, const struct ofperr_domain *domain,
+                    const char *s)
+{
+    if (!domain) {
+        domain = &ofperr_of10;
+    }
+    return ofperr_encode_msg__(error, domain, htonl(0), s, strlen(s));
+}
+
+/* Tries to decodes 'oh', which should be an OpenFlow OFPT_ERROR message.
+ * Returns an OFPERR_* constant on success, 0 on failure.
+ *
+ * If 'payload_ofs' is nonnull, on success '*payload_ofs' is set to the offset
+ * to the payload starting from 'oh' and on failure it is set to 0. */
+enum ofperr
+ofperr_decode_msg(const struct ofp_header *oh, size_t *payload_ofs)
+{
+    static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
+
+    const struct ofperr_domain *domain;
+    const struct ofp_error_msg *oem;
+    uint16_t type, code;
+    enum ofperr error;
+    struct ofpbuf b;
+
+    if (payload_ofs) {
+        *payload_ofs = 0;
+    }
+
+    /* Pull off the error message. */
+    ofpbuf_use_const(&b, oh, ntohs(oh->length));
+    oem = ofpbuf_try_pull(&b, sizeof *oem);
+    if (!oem) {
+        return 0;
+    }
+
+    /* Check message type and version. */
+    if (oh->type != OFPT_ERROR) {
+        return 0;
+    }
+    domain = ofperr_domain_from_version(oh->version);
+    if (!domain) {
+        return 0;
+    }
+
+    /* Get the error type and code. */
+    type = ntohs(oem->type);
+    code = ntohs(oem->code);
+    if (type == NXET_VENDOR && code == NXVC_VENDOR_ERROR) {
+        const struct nx_vendor_error *nve = ofpbuf_try_pull(&b, sizeof *nve);
+        if (!nve) {
+            return 0;
+        }
+
+        if (nve->vendor != htonl(NX_VENDOR_ID)) {
+            VLOG_WARN_RL(&rl, "error contains unknown vendor ID %#"PRIx32,
+                         ntohl(nve->vendor));
+            return 0;
+        }
+        type = ntohs(nve->type);
+        code = ntohs(nve->code);
+    }
+
+    /* Translate the error type and code into an ofperr.
+     * If we don't know the error type and code, at least try for the type. */
+    error = ofperr_decode(domain, type, code);
+    if (!error) {
+        error = ofperr_decode_type(domain, type);
+    }
+    if (error && payload_ofs) {
+        *payload_ofs = (uint8_t *) b.data - (uint8_t *) oh;
+    }
+    return error;
+}
+
+/* If 'error' is a valid OFPERR_* value, returns its name
+ * (e.g. "OFPBRC_BAD_TYPE" for OFPBRC_BAD_TYPE).  Otherwise, assumes that
+ * 'error' is a positive errno value and returns what strerror() produces for
+ * 'error'.  */
+const char *
+ofperr_to_string(enum ofperr error)
+{
+    return ofperr_is_valid(error) ? ofperr_get_name(error) : strerror(error);
+}
+
diff --git a/lib/ofp-errors.h b/lib/ofp-errors.h
index d677b5d..fbd28e3 100644
--- a/lib/ofp-errors.h
+++ b/lib/ofp-errors.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2010 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2011 Nicira Networks.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -17,12 +17,389 @@
 #ifndef OFP_ERRORS_H
 #define OFP_ERRORS_H 1
 
+#include <stdbool.h>
+#include <stddef.h>
 #include <stdint.h>
 
-/* These functions are building blocks for the ofputil_format_error() and
- * ofputil_error_to_string() functions declared in ofp-util.h.  Those functions
- * have friendlier interfaces and should usually be preferred. */
-const char *ofp_error_type_to_string(uint16_t value);
-const char *ofp_error_code_to_string(uint16_t type, uint16_t code);
+struct ds;
+struct ofp_header;
+
+/* Error codes.
+ *
+ * We embed system errno values and OpenFlow standard and vendor extension
+ * error codes into the positive range of "int":
+ *
+ *   - Errno values are assumed to use the range 1 through 2**30 - 1.
+ *
+ *     (C and POSIX say that errno values are positive.  We assume that they
+ *     are less than 2**29.  They are actually less than 65536 on at least
+ *     Linux, FreeBSD, OpenBSD, and Windows.)
+ *
+ *   - OpenFlow standard and vendor extension error codes use the range
+ *     starting at 2**30 (OFPERR_OFS).
+ *
+ * Zero and negative values are not used.
+ */
+
+#define OFPERR_OFS (1 << 30)
+
+enum ofperr {
+/* ## ------------------ ## */
+/* ## OFPET_HELLO_FAILED ## */
+/* ## ------------------ ## */
+
+    /* OF(0).  Hello protocol failed. */
+    OFPERR_OFPET_HELLO_FAILED = OFPERR_OFS,
+
+    /* OF(0,0).  No compatible version. */
+    OFPERR_OFPHFC_INCOMPATIBLE,
+
+    /* OF(0,1).  Permissions error. */
+    OFPERR_OFPHFC_EPERM,
+
+/* ## ----------------- ## */
+/* ## OFPET_BAD_REQUEST ## */
+/* ## ----------------- ## */
+
+    /* OF(1).  Request was not understood. */
+    OFPERR_OFPET_BAD_REQUEST,
+
+    /* OF(1,0).  ofp_header.version not supported. */
+    OFPERR_OFPBRC_BAD_VERSION,
+
+    /* OF(1,1).  ofp_header.type not supported. */
+    OFPERR_OFPBRC_BAD_TYPE,
+
+    /* OF(1,2).  ofp_stats_msg.type not supported. */
+    OFPERR_OFPBRC_BAD_STAT,
+
+    /* OF(1,3).  Vendor not supported (in ofp_vendor_header or
+     * ofp_stats_msg). */
+    OFPERR_OFPBRC_BAD_VENDOR,
+
+    /* OF(1,4).  Vendor subtype not supported. */
+    OFPERR_OFPBRC_BAD_SUBTYPE,
+
+    /* OF(1,5).  Permissions error. */
+    OFPERR_OFPBRC_EPERM,
+
+    /* OF(1,6).  Wrong request length for type. */
+    OFPERR_OFPBRC_BAD_LEN,
+
+    /* OF(1,7).  Specified buffer has already been used. */
+    OFPERR_OFPBRC_BUFFER_EMPTY,
+
+    /* OF(1,8).  Specified buffer does not exist. */
+    OFPERR_OFPBRC_BUFFER_UNKNOWN,
+
+    /* OF1.1(1,9).  Specified table-id invalid or does not exist. */
+    OFPERR_OFPBRC_BAD_TABLE_ID,
+
+    /* NX(1,256).  Invalid NXM flow match. */
+    OFPERR_NXBRC_NXM_INVALID,
+
+    /* NX(1,257).  The nxm_type, or nxm_type taken in combination with
+     * nxm_hasmask or nxm_length or both, is invalid or not implemented. */
+    OFPERR_NXBRC_NXM_BAD_TYPE,
+
+    /* NX(1,258).  Invalid nxm_value. */
+    OFPERR_NXBRC_NXM_BAD_VALUE,
+
+    /* NX(1,259).  Invalid nxm_mask. */
+    OFPERR_NXBRC_NXM_BAD_MASK,
+
+    /* NX(1,260).  A prerequisite was not met. */
+    OFPERR_NXBRC_NXM_BAD_PREREQ,
+
+    /* NX(1,261).  A given nxm_type was specified more than once. */
+    OFPERR_NXBRC_NXM_DUP_TYPE,
+
+    /* NX(1,512).  A request specified a nonexistent table ID. */
+    OFPERR_NXBRC_BAD_TABLE_ID,
+
+    /* NX(1,513).  NXT_ROLE_REQUEST specified an invalid role. */
+    OFPERR_NXBRC_BAD_ROLE,
+
+    /* NX(1,514).  The in_port in an ofp_packet_out request is invalid. */
+    OFPERR_NXBRC_BAD_IN_PORT,
+
+/* ## ---------------- ## */
+/* ## OFPET_BAD_ACTION ## */
+/* ## ---------------- ## */
+
+    /* OF(2).  Error in action description. */
+    OFPERR_OFPET_BAD_ACTION,
+
+    /* OF(2,0).  Unknown action type. */
+    OFPERR_OFPBAC_BAD_TYPE,
+
+    /* OF(2,1).  Length problem in actions. */
+    OFPERR_OFPBAC_BAD_LEN,
+
+    /* OF(2,2).  Unknown experimenter id specified. */
+    OFPERR_OFPBAC_BAD_VENDOR,
+
+    /* OF(2,3).  Unknown action type for experimenter id. */
+    OFPERR_OFPBAC_BAD_VENDOR_TYPE,
+
+    /* OF(2,4).  Problem validating output port. */
+    OFPERR_OFPBAC_BAD_OUT_PORT,
+
+    /* OF(2,5).  Bad action argument. */
+    OFPERR_OFPBAC_BAD_ARGUMENT,
+
+    /* OF(2,6).  Permissions error. */
+    OFPERR_OFPBAC_EPERM,
+
+    /* OF(2,7).  Can't handle this many actions. */
+    OFPERR_OFPBAC_TOO_MANY,
+
+    /* OF(2,8).  Problem validating output queue. */
+    OFPERR_OFPBAC_BAD_QUEUE,
+
+    /* OF1.1(2,9).  Invalid group id in forward action. */
+    OFPERR_OFPBAC_BAD_OUT_GROUP,
+
+    /* OF1.1(2,10).  Action can't apply for this match. */
+    OFPERR_OFPBAC_MATCH_INCONSISTENT,
+
+    /* OF1.1(2,11).  Action order is unsupported for the action list in an
+     * Apply-Actions instruction */
+    OFPERR_OFPBAC_UNSUPPORTED_ORDER,
+
+    /* OF1.1(2,12).  Actions uses an unsupported tag/encap. */
+    OFPERR_OFPBAC_BAD_TAG,
+
+/* ## --------------------- ## */
+/* ## OFPET_BAD_INSTRUCTION ## */
+/* ## --------------------- ## */
+
+    /* OF1.1(3).  Error in instruction list. */
+    OFPERR_OFPET_BAD_INSTRUCTION,
+
+    /* OF1.1(3,0).  Unknown instruction. */
+    OFPERR_OFPBIC_UNKNOWN_INST,
+
+    /* OF1.1(3,1).  Switch or table does not support the instruction. */
+    OFPERR_OFPBIC_UNSUP_INST,
+
+    /* OF1.1(3,2).  Invalid Table-ID specified. */
+    OFPERR_OFPBIC_BAD_TABLE_ID,
+
+    /* OF1.1(3,3).  Metadata value unsupported by datapath. */
+    OFPERR_OFPBIC_UNSUP_METADATA,
+
+    /* OF1.1(3,4).  Metadata mask value unsupported by datapath. */
+    OFPERR_OFPBIC_UNSUP_METADATA_MASK,
+
+    /* OF1.1(3,5).  Specific experimenter instruction unsupported. */
+    OFPERR_OFPBIC_UNSUP_EXP_INST,
+
+/* ## --------------- ## */
+/* ## OFPET_BAD_MATCH ## */
+/* ## --------------- ## */
+
+    /* OF1.1(4).  Error in match. */
+    OFPERR_OFPET_BAD_MATCH,
+
+    /* OF1.1(4,0).  Unsupported match type specified by the match */
+    OFPERR_OFPBMC_BAD_TYPE,
+
+    /* OF1.1(4,1).  Length problem in match. */
+    OFPERR_OFPBMC_BAD_LEN,
+
+    /* OF1.1(4,2).  Match uses an unsupported tag/encap. */
+    OFPERR_OFPBMC_BAD_TAG,
+
+    /* OF1.1(4,3).  Unsupported datalink addr mask - switch does not support
+     * arbitrary datalink address mask. */
+    OFPERR_OFPBMC_BAD_DL_ADDR_MASK,
+
+    /* OF1.1(4,4).  Unsupported network addr mask - switch does not support
+     * arbitrary network address mask. */
+    OFPERR_OFPBMC_BAD_NW_ADDR_MASK,
+
+    /* OF1.1(4,5).  Unsupported wildcard specified in the match. */
+    OFPERR_OFPBMC_BAD_WILDCARDS,
+
+    /* OF1.1(4,6).  Unsupported field in the match. */
+    OFPERR_OFPBMC_BAD_FIELD,
+
+    /* OF1.1(4,7).  Unsupported value in a match field. */
+    OFPERR_OFPBMC_BAD_VALUE,
+
+/* ## --------------------- ## */
+/* ## OFPET_FLOW_MOD_FAILED ## */
+/* ## --------------------- ## */
+
+    /* OF1.0(3), OF1.1(5).  Problem modifying flow entry. */
+    OFPERR_OFPET_FLOW_MOD_FAILED,
+
+    /* OF1.1(5,0).  Unspecified error. */
+    OFPERR_OFPFMFC_UNKNOWN,
+
+    /* OF1.0(3,0).  Flow not added because of full tables. */
+    OFPERR_OFPFMFC_ALL_TABLES_FULL,
+
+    /* OF1.1(5,1).  Flow not added because table was full. */
+    OFPERR_OFPFMFC_TABLE_FULL,
+
+    /* OF1.1(5,2).  Table does not exist */
+    OFPERR_OFPFMFC_BAD_TABLE_ID,
+
+    /* OF1.0(3,1), OF1.1(5,3).  Attempted to add overlapping flow with
+     * CHECK_OVERLAP flag set. */
+    OFPERR_OFPFMFC_OVERLAP,
+
+    /* OF1.0(3,2), OF1.1(5,4).  Permissions error. */
+    OFPERR_OFPFMFC_EPERM,
+
+    /* OF1.1(5,5).  Flow not added because of unsupported idle/hard timeout. */
+    OFPERR_OFPFMFC_BAD_TIMEOUT,
+
+    /* OF1.0(3,3).  Flow not added because of non-zero idle/hard timeout. */
+    OFPERR_OFPFMFC_BAD_EMERG_TIMEOUT,
+
+    /* OF1.0(3,4), OF1.1(5,6).  Unsupported or unknown command. */
+    OFPERR_OFPFMFC_BAD_COMMAND,
+
+    /* OF1.0(3,5).  Unsupported action list - cannot process in the order
+     * specified. */
+    OFPERR_OFPFMFC_UNSUPPORTED,
+
+    /* NX1.0(3,256), NX1.1(5,256).  Generic hardware error. */
+    OFPERR_NXFMFC_HARDWARE,
+
+    /* NX1.0(3,257), NX1.1(5,257).  A nonexistent table ID was specified in the
+     * "command" field of struct ofp_flow_mod, when the nxt_flow_mod_table_id
+     * extension is enabled. */
+    OFPERR_NXFMFC_BAD_TABLE_ID,
+
+/* ## ---------------------- ## */
+/* ## OFPET_GROUP_MOD_FAILED ## */
+/* ## ---------------------- ## */
+
+    /* OF1.1(6).  Problem modifying group entry. */
+    OFPERR_OFPET_GROUP_MOD_FAILED,
+
+    /* OF1.1(6,0).  Group not added because a group ADD attempted to replace an
+     * already-present group. */
+    OFPERR_OFPGMFC_GROUP_EXISTS,
+
+    /* OF1.1(6,1).  Group not added because Group specified is invalid. */
+    OFPERR_OFPGMFC_INVALID_GROUP,
+
+    /* OF1.1(6,2).  Switch does not support unequal load sharing with select
+     * groups. */
+    OFPERR_OFPGMFC_WEIGHT_UNSUPPORTED,
+
+    /* OF1.1(6,3).  The group table is full. */
+    OFPERR_OFPGMFC_OUT_OF_GROUPS,
+
+    /* OF1.1(6,4).  The maximum number of action buckets for a group has been
+     * exceeded. */
+    OFPERR_OFPGMFC_OUT_OF_BUCKETS,
+
+    /* OF1.1(6,5).  Switch does not support groups that forward to groups. */
+    OFPERR_OFPGMFC_CHAINING_UNSUPPORTED,
+
+    /* OF1.1(6,6).  This group cannot watch the watch_port or watch_group
+     * specified. */
+    OFPERR_OFPGMFC_WATCH_UNSUPPORTED,
+
+    /* OF1.1(6,7).  Group entry would cause a loop. */
+    OFPERR_OFPGMFC_LOOP,
+
+    /* OF1.1(6,8).  Group not modified because a group MODIFY attempted to
+     * modify a non-existent group. */
+    OFPERR_OFPGMFC_UNKNOWN_GROUP,
+
+/* ## --------------------- ## */
+/* ## OFPET_PORT_MOD_FAILED ## */
+/* ## --------------------- ## */
+
+    /* OF1.0(4), OF1.1(7).  OFPT_PORT_MOD failed. */
+    OFPERR_OFPET_PORT_MOD_FAILED,
+
+    /* OF1.0(4,0), OF1.1(7,0).  Specified port does not exist. */
+    OFPERR_OFPPMFC_BAD_PORT,
+
+    /* OF1.0(4,1), OF1.1(7,1).  Specified hardware address does not match the
+     * port number. */
+    OFPERR_OFPPMFC_BAD_HW_ADDR,
+
+    /* OF1.1(7,2).  Specified config is invalid. */
+    OFPERR_OFPPMFC_BAD_CONFIG,
+
+    /* OF1.1(7,3).  Specified advertise is invalid. */
+    OFPERR_OFPPMFC_BAD_ADVERTISE,
+
+/* ## ---------------------- ## */
+/* ## OFPET_TABLE_MOD_FAILED ## */
+/* ## ---------------------- ## */
+
+    /* OF1.1(8).  Table mod request failed. */
+    OFPERR_OFPET_TABLE_MOD_FAILED,
+
+    /* OF1.1(8,0).  Specified table does not exist. */
+    OFPERR_OFPTMFC_BAD_TABLE,
+
+    /* OF1.1(8,1).  Specified config is invalid. */
+    OFPERR_OFPTMFC_BAD_CONFIG,
+
+/* ## --------------------- ## */
+/* ## OFPET_QUEUE_OP_FAILED ## */
+/* ## --------------------- ## */
+
+    /* OF1.0(5), OF1.1(9).  Queue operation failed. */
+    OFPERR_OFPET_QUEUE_OP_FAILED,
+
+    /* OF1.0(5,0), OF1.1(9,0).  Invalid port (or port does not exist). */
+    OFPERR_OFPQOFC_BAD_PORT,
+
+    /* OF1.0(5,1), OF1.1(9,1).  Queue does not exist. */
+    OFPERR_OFPQOFC_BAD_QUEUE,
+
+    /* OF1.0(5,2), OF1.1(9,2).  Permissions error. */
+    OFPERR_OFPQOFC_EPERM,
+
+/* ## -------------------------- ## */
+/* ## OFPET_SWITCH_CONFIG_FAILED ## */
+/* ## -------------------------- ## */
+
+    /* OF1.1(10).  Switch config request failed. */
+    OFPERR_OFPET_SWITCH_CONFIG_FAILED,
+
+    /* OF1.1(10,0).  Specified flags is invalid. */
+    OFPERR_OFPSCFC_BAD_FLAGS,
+
+    /* OF1.1(10,1).  Specified len is invalid. */
+    OFPERR_OFPSCFC_BAD_LEN,
+};
+
+extern const struct ofperr_domain ofperr_of10;
+extern const struct ofperr_domain ofperr_of11;
+
+const struct ofperr_domain *ofperr_domain_from_version(uint8_t version);
+
+bool ofperr_is_valid(enum ofperr);
+bool ofperr_is_category(enum ofperr);
+bool ofperr_is_nx_extension(enum ofperr);
+bool ofperr_is_encodable(enum ofperr, const struct ofperr_domain *);
+
+enum ofperr ofperr_decode(const struct ofperr_domain *,
+                          uint16_t type, uint16_t code);
+enum ofperr ofperr_decode_type(const struct ofperr_domain *, uint16_t type);
+
+enum ofperr ofperr_decode_msg(const struct ofp_header *, size_t *payload_ofs);
+struct ofpbuf *ofperr_encode_reply(enum ofperr, const struct ofp_header *);
+struct ofpbuf *ofperr_encode_hello(enum ofperr, const struct ofperr_domain *,
+                                   const char *);
+
+const char *ofperr_get_name(enum ofperr);
+const char *ofperr_get_description(enum ofperr);
+
+void ofperr_format(struct ds *, enum ofperr);
+const char *ofperr_to_string(enum ofperr);
 
 #endif /* ofp-errors.h */
diff --git a/lib/ofp-print.c b/lib/ofp-print.c
index 67f5883..c231a15 100644
--- a/lib/ofp-print.c
+++ b/lib/ofp-print.c
@@ -34,6 +34,7 @@
 #include "learn.h"
 #include "multipath.h"
 #include "nx-match.h"
+#include "ofp-errors.h"
 #include "ofp-util.h"
 #include "ofpbuf.h"
 #include "openflow/openflow.h"
@@ -45,7 +46,7 @@
 #include "util.h"
 
 static void ofp_print_queue_name(struct ds *string, uint32_t port);
-static void ofp_print_error(struct ds *, int error);
+static void ofp_print_error(struct ds *, enum ofperr);
 
 
 /* Returns a string that represents the contents of the Ethernet frame in the
@@ -752,7 +753,7 @@ ofp_print_flow_mod(struct ds *s, const struct ofp_header *oh,
 {
     struct ofputil_flow_mod fm;
     bool need_priority;
-    int error;
+    enum ofperr error;
 
     error = ofputil_decode_flow_mod(&fm, oh, true);
     if (error) {
@@ -851,7 +852,7 @@ static void
 ofp_print_flow_removed(struct ds *string, const struct ofp_header *oh)
 {
     struct ofputil_flow_removed fr;
-    int error;
+    enum ofperr error;
 
     error = ofputil_decode_flow_removed(&fr, oh);
     if (error) {
@@ -902,14 +903,12 @@ ofp_print_port_mod(struct ds *string, const struct ofp_port_mod *opm)
 }
 
 static void
-ofp_print_error(struct ds *string, int error)
+ofp_print_error(struct ds *string, enum ofperr error)
 {
     if (string->length) {
         ds_put_char(string, ' ');
     }
-    ds_put_cstr(string, "***decode error: ");
-    ofputil_format_error(string, error);
-    ds_put_cstr(string, "***\n");
+    ds_put_format(string, "***decode error: %s***\n", ofperr_get_name(error));
 }
 
 static void
@@ -918,32 +917,26 @@ ofp_print_error_msg(struct ds *string, const struct ofp_error_msg *oem)
     size_t len = ntohs(oem->header.length);
     size_t payload_ofs, payload_len;
     const void *payload;
-    int error;
+    enum ofperr error;
     char *s;
 
-    error = ofputil_decode_error_msg(&oem->header, &payload_ofs);
-    if (!is_ofp_error(error)) {
-        ofp_print_error(string, error);
+    error = ofperr_decode_msg(&oem->header, &payload_ofs);
+    if (!error) {
+        ds_put_cstr(string, "***decode error***");
         ds_put_hex_dump(string, oem->data, len - sizeof *oem, 0, true);
         return;
     }
 
-    ds_put_char(string, ' ');
-    ofputil_format_error(string, error);
-    ds_put_char(string, '\n');
+    ds_put_format(string, " %s\n", ofperr_get_name(error));
 
     payload = (const uint8_t *) oem + payload_ofs;
     payload_len = len - payload_ofs;
-    switch (get_ofp_err_type(error)) {
-    case OFPET_HELLO_FAILED:
+    if (error == OFPERR_OFPHFC_INCOMPATIBLE || error == OFPERR_OFPHFC_EPERM) {
         ds_put_printable(string, payload, payload_len);
-        break;
-
-    default:
+    } else {
         s = ofp_to_string(payload, payload_len, 1);
         ds_put_cstr(string, s);
         free(s);
-        break;
     }
 }
 
@@ -982,7 +975,7 @@ ofp_print_flow_stats_request(struct ds *string,
                              const struct ofp_stats_msg *osm)
 {
     struct ofputil_flow_stats_request fsr;
-    int error;
+    enum ofperr error;
 
     error = ofputil_decode_flow_stats_request(&fsr, &osm->header);
     if (error) {
@@ -1467,7 +1460,7 @@ ofp_to_string(const void *oh_, size_t len, int verbosity)
                       ntohs(oh->length), len);
     } else {
         const struct ofputil_msg_type *type;
-        int error;
+        enum ofperr error;
 
         error = ofputil_decode_msg_type(oh, &type);
         if (!error) {
diff --git a/lib/ofp-util.c b/lib/ofp-util.c
index b99eadc..0bce510 100644
--- a/lib/ofp-util.c
+++ b/lib/ofp-util.c
@@ -288,10 +288,10 @@ 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. */
+    enum ofperr missing_error;  /* Error value for missing type. */
 };
 
-static int
+static enum ofperr
 ofputil_check_length(const struct ofputil_msg_type *type, unsigned int size)
 {
     switch (type->extra_multiple) {
@@ -300,7 +300,7 @@ ofputil_check_length(const struct ofputil_msg_type *type, unsigned int size)
             VLOG_WARN_RL(&bad_ofmsg_rl, "received %s with incorrect "
                          "length %u (expected length %u)",
                          type->name, size, type->min_size);
-            return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_LEN);
+            return OFPERR_OFPBRC_BAD_LEN;
         }
         return 0;
 
@@ -309,7 +309,7 @@ ofputil_check_length(const struct ofputil_msg_type *type, unsigned int size)
             VLOG_WARN_RL(&bad_ofmsg_rl, "received %s with incorrect "
                          "length %u (expected length at least %u bytes)",
                          type->name, size, type->min_size);
-            return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_LEN);
+            return OFPERR_OFPBRC_BAD_LEN;
         }
         return 0;
 
@@ -321,13 +321,13 @@ ofputil_check_length(const struct ofputil_msg_type *type, unsigned int size)
                          "by an integer multiple of %u bytes)",
                          type->name, size,
                          type->min_size, type->extra_multiple);
-            return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_LEN);
+            return OFPERR_OFPBRC_BAD_LEN;
         }
         return 0;
     }
 }
 
-static int
+static enum ofperr
 ofputil_lookup_openflow_message(const struct ofputil_msg_category *cat,
                                 uint32_t value,
                                 const struct ofputil_msg_type **typep)
@@ -346,7 +346,7 @@ ofputil_lookup_openflow_message(const struct ofputil_msg_category *cat,
     return cat->missing_error;
 }
 
-static int
+static enum ofperr
 ofputil_decode_vendor(const struct ofp_header *oh, size_t length,
                       const struct ofputil_msg_type **typep)
 {
@@ -379,7 +379,7 @@ ofputil_decode_vendor(const struct ofp_header *oh, size_t length,
     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)
+        OFPERR_OFPBRC_BAD_SUBTYPE
     };
 
     const struct ofp_vendor_header *ovh;
@@ -389,14 +389,14 @@ ofputil_decode_vendor(const struct ofp_header *oh, size_t length,
         if (length == ntohs(oh->length)) {
             VLOG_WARN_RL(&bad_ofmsg_rl, "truncated vendor message");
         }
-        return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_LEN);
+        return OFPERR_OFPBRC_BAD_LEN;
     }
 
     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);
+        return OFPERR_OFPBRC_BAD_VENDOR;
     }
 
     if (length < sizeof(struct nicira_header)) {
@@ -406,7 +406,7 @@ ofputil_decode_vendor(const struct ofp_header *oh, size_t length,
                          ntohs(ovh->header.length),
                          sizeof(struct nicira_header));
         }
-        return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_LEN);
+        return OFPERR_OFPBRC_BAD_LEN;
     }
 
     nh = (const struct nicira_header *) oh;
@@ -414,7 +414,7 @@ ofputil_decode_vendor(const struct ofp_header *oh, size_t length,
                                            typep);
 }
 
-static int
+static enum ofperr
 check_nxstats_msg(const struct ofp_header *oh, size_t length)
 {
     const struct ofp_stats_msg *osm = (const struct ofp_stats_msg *) oh;
@@ -424,27 +424,27 @@ check_nxstats_msg(const struct ofp_header *oh, size_t length)
         if (length == ntohs(oh->length)) {
             VLOG_WARN_RL(&bad_ofmsg_rl, "truncated vendor stats message");
         }
-        return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_LEN);
+        return OFPERR_OFPBRC_BAD_LEN;
     }
 
     memcpy(&vendor, osm + 1, 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);
+        return OFPERR_OFPBRC_BAD_VENDOR;
     }
 
     if (length < sizeof(struct nicira_stats_msg)) {
         if (length == ntohs(osm->header.length)) {
             VLOG_WARN_RL(&bad_ofmsg_rl, "truncated Nicira stats message");
         }
-        return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_LEN);
+        return OFPERR_OFPBRC_BAD_LEN;
     }
 
     return 0;
 }
 
-static int
+static enum ofperr
 ofputil_decode_nxst_request(const struct ofp_header *oh, size_t length,
                             const struct ofputil_msg_type **typep)
 {
@@ -461,11 +461,11 @@ ofputil_decode_nxst_request(const struct ofp_header *oh, size_t length,
     static const struct ofputil_msg_category nxst_request_category = {
         "Nicira extension statistics request",
         nxst_requests, ARRAY_SIZE(nxst_requests),
-        OFP_MKERR(OFPET_BAD_REQUEST, OFPBRC_BAD_SUBTYPE)
+        OFPERR_OFPBRC_BAD_SUBTYPE
     };
 
     const struct nicira_stats_msg *nsm;
-    int error;
+    enum ofperr error;
 
     error = check_nxstats_msg(oh, length);
     if (error) {
@@ -477,7 +477,7 @@ ofputil_decode_nxst_request(const struct ofp_header *oh, size_t length,
                                            ntohl(nsm->subtype), typep);
 }
 
-static int
+static enum ofperr
 ofputil_decode_nxst_reply(const struct ofp_header *oh, size_t length,
                           const struct ofputil_msg_type **typep)
 {
@@ -494,11 +494,11 @@ ofputil_decode_nxst_reply(const struct ofp_header *oh, size_t length,
     static const struct ofputil_msg_category nxst_reply_category = {
         "Nicira extension statistics reply",
         nxst_replies, ARRAY_SIZE(nxst_replies),
-        OFP_MKERR(OFPET_BAD_REQUEST, OFPBRC_BAD_SUBTYPE)
+        OFPERR_OFPBRC_BAD_SUBTYPE
     };
 
     const struct nicira_stats_msg *nsm;
-    int error;
+    enum ofperr error;
 
     error = check_nxstats_msg(oh, length);
     if (error) {
@@ -510,20 +510,20 @@ ofputil_decode_nxst_reply(const struct ofp_header *oh, size_t length,
                                            ntohl(nsm->subtype), typep);
 }
 
-static int
+static enum ofperr
 check_stats_msg(const struct ofp_header *oh, size_t length)
 {
     if (length < sizeof(struct ofp_stats_msg)) {
         if (length == ntohs(oh->length)) {
             VLOG_WARN_RL(&bad_ofmsg_rl, "truncated stats message");
         }
-        return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_LEN);
+        return OFPERR_OFPBRC_BAD_LEN;
     }
 
     return 0;
 }
 
-static int
+static enum ofperr
 ofputil_decode_ofpst_request(const struct ofp_header *oh, size_t length,
                              const struct ofputil_msg_type **typep)
 {
@@ -560,11 +560,11 @@ ofputil_decode_ofpst_request(const struct ofp_header *oh, size_t length,
     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)
+        OFPERR_OFPBRC_BAD_STAT
     };
 
     const struct ofp_stats_msg *request = (const struct ofp_stats_msg *) oh;
-    int error;
+    enum ofperr error;
 
     error = check_stats_msg(oh, length);
     if (error) {
@@ -579,7 +579,7 @@ ofputil_decode_ofpst_request(const struct ofp_header *oh, size_t length,
     return error;
 }
 
-static int
+static enum ofperr
 ofputil_decode_ofpst_reply(const struct ofp_header *oh, size_t length,
                            const struct ofputil_msg_type **typep)
 {
@@ -616,11 +616,11 @@ ofputil_decode_ofpst_reply(const struct ofp_header *oh, size_t length,
     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)
+        OFPERR_OFPBRC_BAD_STAT
     };
 
     const struct ofp_stats_msg *reply = (const struct ofp_stats_msg *) oh;
-    int error;
+    enum ofperr error;
 
     error = check_stats_msg(oh, length);
     if (error) {
@@ -635,7 +635,7 @@ ofputil_decode_ofpst_reply(const struct ofp_header *oh, size_t length,
     return error;
 }
 
-static int
+static enum ofperr
 ofputil_decode_msg_type__(const struct ofp_header *oh, size_t length,
                           const struct ofputil_msg_type **typep)
 {
@@ -724,10 +724,10 @@ ofputil_decode_msg_type__(const struct ofp_header *oh, size_t length,
     static const struct ofputil_msg_category ofpt_category = {
         "OpenFlow message",
         ofpt_messages, ARRAY_SIZE(ofpt_messages),
-        OFP_MKERR(OFPET_BAD_REQUEST, OFPBRC_BAD_TYPE)
+        OFPERR_OFPBRC_BAD_TYPE
     };
 
-    int error;
+    enum ofperr error;
 
     error = ofputil_lookup_openflow_message(&ofpt_category, oh->type, typep);
     if (!error) {
@@ -750,22 +750,21 @@ ofputil_decode_msg_type__(const struct ofp_header *oh, size_t length,
     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.
+/* Decodes the message type represented by 'oh'.  Returns 0 if successful or an
+ * OpenFlow error code on failure.  Either way, stores in '*typep' a type
+ * structure that can be inspected with the ofputil_msg_type_*() functions.
  *
  * oh->length must indicate the correct length of the message (and must be at
  * least sizeof(struct ofp_header)).
  *
  * Success indicates that 'oh' is at least as long as the minimum-length
  * message of its type. */
-int
+enum ofperr
 ofputil_decode_msg_type(const struct ofp_header *oh,
                         const struct ofputil_msg_type **typep)
 {
     size_t length = ntohs(oh->length);
-    int error;
+    enum ofperr error;
 
     error = ofputil_decode_msg_type__(oh, length, typep);
     if (!error) {
@@ -779,18 +778,17 @@ ofputil_decode_msg_type(const struct ofp_header *oh,
 
 /* Decodes the message type represented by 'oh', of which only the first
  * 'length' bytes are available.  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.  */
-int
+ * code on failure.  Either way, stores in '*typep' a type structure that can
+ * be inspected with the ofputil_msg_type_*() functions.  */
+enum ofperr
 ofputil_decode_msg_type_partial(const struct ofp_header *oh, size_t length,
                                 const struct ofputil_msg_type **typep)
 {
-    int error;
+    enum ofperr error;
 
     error = (length >= sizeof *oh
              ? ofputil_decode_msg_type__(oh, length, typep)
-             : ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_LEN));
+             : OFPERR_OFPBRC_BAD_LEN);
     if (error) {
         *typep = &ofputil_invalid_type;
     }
@@ -948,7 +946,7 @@ ofputil_make_flow_mod_table_id(bool flow_mod_table_id)
  * enabled, false otherwise.
  *
  * Does not validate the flow_mod actions. */
-int
+enum ofperr
 ofputil_decode_flow_mod(struct ofputil_flow_mod *fm,
                         const struct ofp_header *oh, bool flow_mod_table_id)
 {
@@ -963,7 +961,7 @@ ofputil_decode_flow_mod(struct ofputil_flow_mod *fm,
         /* Standard OpenFlow flow_mod. */
         const struct ofp_flow_mod *ofm;
         uint16_t priority;
-        int error;
+        enum ofperr error;
 
         /* Dissect the message. */
         ofm = ofpbuf_pull(&b, sizeof *ofm);
@@ -996,7 +994,7 @@ ofputil_decode_flow_mod(struct ofputil_flow_mod *fm,
     } else if (ofputil_msg_type_code(type) == OFPUTIL_NXT_FLOW_MOD) {
         /* Nicira extended flow_mod. */
         const struct nx_flow_mod *nfm;
-        int error;
+        enum ofperr error;
 
         /* Dissect the message. */
         nfm = ofpbuf_pull(&b, sizeof *nfm);
@@ -1092,7 +1090,7 @@ ofputil_encode_flow_mod(const struct ofputil_flow_mod *fm,
     return msg;
 }
 
-static int
+static enum ofperr
 ofputil_decode_ofpst_flow_request(struct ofputil_flow_stats_request *fsr,
                                   const struct ofp_header *oh,
                                   bool aggregate)
@@ -1108,14 +1106,14 @@ ofputil_decode_ofpst_flow_request(struct ofputil_flow_stats_request *fsr,
     return 0;
 }
 
-static int
+static enum ofperr
 ofputil_decode_nxst_flow_request(struct ofputil_flow_stats_request *fsr,
                                  const struct ofp_header *oh,
                                  bool aggregate)
 {
     const struct nx_flow_stats_request *nfsr;
     struct ofpbuf b;
-    int error;
+    enum ofperr error;
 
     ofpbuf_use_const(&b, oh, ntohs(oh->length));
 
@@ -1125,7 +1123,7 @@ ofputil_decode_nxst_flow_request(struct ofputil_flow_stats_request *fsr,
         return error;
     }
     if (b.size) {
-        return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_LEN);
+        return OFPERR_OFPBRC_BAD_LEN;
     }
 
     fsr->aggregate = aggregate;
@@ -1138,7 +1136,7 @@ ofputil_decode_nxst_flow_request(struct ofputil_flow_stats_request *fsr,
 /* Converts an OFPST_FLOW, OFPST_AGGREGATE, NXST_FLOW, or NXST_AGGREGATE
  * request 'oh', into an abstract flow_stats_request in 'fsr'.  Returns 0 if
  * successful, otherwise an OpenFlow error code. */
-int
+enum ofperr
 ofputil_decode_flow_stats_request(struct ofputil_flow_stats_request *fsr,
                                   const struct ofp_header *oh)
 {
@@ -1422,7 +1420,7 @@ ofputil_encode_aggregate_stats_reply(
 /* Converts an OFPT_FLOW_REMOVED or NXT_FLOW_REMOVED message 'oh' into an
  * abstract ofputil_flow_removed in 'fr'.  Returns 0 if successful, otherwise
  * an OpenFlow error code. */
-int
+enum ofperr
 ofputil_decode_flow_removed(struct ofputil_flow_removed *fr,
                             const struct ofp_header *oh)
 {
@@ -1458,7 +1456,7 @@ ofputil_decode_flow_removed(struct ofputil_flow_removed *fr,
             return error;
         }
         if (b.size) {
-            return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_LEN);
+            return OFPERR_OFPBRC_BAD_LEN;
         }
 
         fr->cookie = nfr->cookie;
@@ -2055,8 +2053,8 @@ ofputil_frag_handling_from_string(const char *s, enum ofp_config_flags *flags)
 
 /* Checks that 'port' is a valid output port for the OFPAT_OUTPUT action, given
  * that the switch will never have more than 'max_ports' ports.  Returns 0 if
- * 'port' is valid, otherwise an ofp_mkerr() return code. */
-int
+ * 'port' is valid, otherwise an OpenFlow return code. */
+enum ofperr
 ofputil_check_output_port(uint16_t port, int max_ports)
 {
     switch (port) {
@@ -2073,7 +2071,7 @@ ofputil_check_output_port(uint16_t port, int max_ports)
         if (port < max_ports) {
             return 0;
         }
-        return ofp_mkerr(OFPET_BAD_ACTION, OFPBAC_BAD_OUT_PORT);
+        return OFPERR_OFPBAC_BAD_OUT_PORT;
     }
 }
 
@@ -2139,16 +2137,16 @@ ofputil_format_port(uint16_t port, struct ds *s)
     ds_put_cstr(s, name);
 }
 
-static int
+static enum ofperr
 check_resubmit_table(const struct nx_action_resubmit *nar)
 {
     if (nar->pad[0] || nar->pad[1] || nar->pad[2]) {
-        return ofp_mkerr(OFPET_BAD_ACTION, OFPBAC_BAD_ARGUMENT);
+        return OFPERR_OFPBAC_BAD_ARGUMENT;
     }
     return 0;
 }
 
-static int
+static enum ofperr
 check_output_reg(const struct nx_action_output_reg *naor,
                  const struct flow *flow)
 {
@@ -2156,7 +2154,7 @@ check_output_reg(const struct nx_action_output_reg *naor,
 
     for (i = 0; i < sizeof naor->zero; i++) {
         if (naor->zero[i]) {
-            return ofp_mkerr(OFPET_BAD_ACTION, OFPBAC_BAD_ARGUMENT);
+            return OFPERR_OFPBAC_BAD_ARGUMENT;
         }
     }
 
@@ -2164,7 +2162,7 @@ check_output_reg(const struct nx_action_output_reg *naor,
                          nxm_decode_n_bits(naor->ofs_nbits), flow);
 }
 
-int
+enum ofperr
 validate_actions(const union ofp_action *actions, size_t n_actions,
                  const struct flow *flow, int max_ports)
 {
@@ -2172,20 +2170,16 @@ validate_actions(const union ofp_action *actions, size_t n_actions,
     size_t left;
 
     OFPUTIL_ACTION_FOR_EACH (a, left, actions, n_actions) {
+        enum ofperr error;
         uint16_t port;
-        int error;
         int code;
 
         code = ofputil_decode_action(a);
         if (code < 0) {
-            char *msg;
-
             error = -code;
-            msg = ofputil_error_to_string(error);
             VLOG_WARN_RL(&bad_ofmsg_rl,
                          "action decoding error at offset %td (%s)",
-                         (a - actions) * sizeof *a, msg);
-            free(msg);
+                         (a - actions) * sizeof *a, ofperr_get_name(error));
 
             return error;
         }
@@ -2199,13 +2193,13 @@ validate_actions(const union ofp_action *actions, size_t n_actions,
 
         case OFPUTIL_OFPAT_SET_VLAN_VID:
             if (a->vlan_vid.vlan_vid & ~htons(0xfff)) {
-                error = ofp_mkerr(OFPET_BAD_ACTION, OFPBAC_BAD_ARGUMENT);
+                error = OFPERR_OFPBAC_BAD_ARGUMENT;
             }
             break;
 
         case OFPUTIL_OFPAT_SET_VLAN_PCP:
             if (a->vlan_pcp.vlan_pcp & ~7) {
-                error = ofp_mkerr(OFPET_BAD_ACTION, OFPBAC_BAD_ARGUMENT);
+                error = OFPERR_OFPBAC_BAD_ARGUMENT;
             }
             break;
 
@@ -2213,7 +2207,7 @@ validate_actions(const union ofp_action *actions, size_t n_actions,
             port = ntohs(((const struct ofp_action_enqueue *) a)->port);
             if (port >= max_ports && port != OFPP_IN_PORT
                 && port != OFPP_LOCAL) {
-                error = ofp_mkerr(OFPET_BAD_ACTION, OFPBAC_BAD_OUT_PORT);
+                error = OFPERR_OFPBAC_BAD_OUT_PORT;
             }
             break;
 
@@ -2276,17 +2270,15 @@ validate_actions(const union ofp_action *actions, size_t n_actions,
         }
 
         if (error) {
-            char *msg = ofputil_error_to_string(error);
             VLOG_WARN_RL(&bad_ofmsg_rl, "bad action at offset %td (%s)",
-                         (a - actions) * sizeof *a, msg);
-            free(msg);
+                         (a - actions) * sizeof *a, ofperr_get_name(error));
             return error;
         }
     }
     if (left) {
         VLOG_WARN_RL(&bad_ofmsg_rl, "bad action format at offset %zu",
                      (n_actions - left) * sizeof *a);
-        return ofp_mkerr(OFPET_BAD_ACTION, OFPBAC_BAD_LEN);
+        return OFPERR_OFPBAC_BAD_LEN;
     }
     return 0;
 }
@@ -2298,11 +2290,11 @@ struct ofputil_action {
 };
 
 static const struct ofputil_action action_bad_type
-    = { -OFP_MKERR(OFPET_BAD_ACTION, OFPBAC_BAD_TYPE),   0, UINT_MAX };
+    = { -OFPERR_OFPBAC_BAD_TYPE,   0, UINT_MAX };
 static const struct ofputil_action action_bad_len
-    = { -OFP_MKERR(OFPET_BAD_ACTION, OFPBAC_BAD_LEN),    0, UINT_MAX };
+    = { -OFPERR_OFPBAC_BAD_LEN,    0, UINT_MAX };
 static const struct ofputil_action action_bad_vendor
-    = { -OFP_MKERR(OFPET_BAD_ACTION, OFPBAC_BAD_VENDOR), 0, UINT_MAX };
+    = { -OFPERR_OFPBAC_BAD_VENDOR, 0, UINT_MAX };
 
 static const struct ofputil_action *
 ofputil_decode_ofpat_action(const union ofp_action *a)
@@ -2353,8 +2345,8 @@ ofputil_decode_nxast_action(const union ofp_action *a)
 }
 
 /* Parses 'a' to determine its type.  Returns a nonnegative OFPUTIL_OFPAT_* or
- * OFPUTIL_NXAST_* constant if successful, otherwise a negative OpenFlow error
- * code (as returned by ofp_mkerr()).
+ * OFPUTIL_NXAST_* constant if successful, otherwise a negative OFPERR_* error
+ * code.
  *
  * The caller must have already verified that 'a''s length is correct (that is,
  * a->header.len is nonzero and a multiple of sizeof(union ofp_action) and no
@@ -2374,7 +2366,7 @@ ofputil_decode_action(const union ofp_action *a)
         switch (ntohl(a->vendor.vendor)) {
         case NX_VENDOR_ID:
             if (len < sizeof(struct nx_action_header)) {
-                return -ofp_mkerr(OFPET_BAD_ACTION, OFPBAC_BAD_LEN);
+                return -OFPERR_OFPBAC_BAD_LEN;
             }
             action = ofputil_decode_nxast_action(a);
             break;
@@ -2386,7 +2378,7 @@ ofputil_decode_action(const union ofp_action *a)
 
     return (len >= action->min_len && len <= action->max_len
             ? action->code
-            : -ofp_mkerr(OFPET_BAD_ACTION, OFPBAC_BAD_LEN));
+            : -OFPERR_OFPBAC_BAD_LEN);
 }
 
 /* Parses 'a' and returns its type as an OFPUTIL_OFPAT_* or OFPUTIL_NXAST_*
@@ -2613,207 +2605,6 @@ ofputil_normalize_rule(struct cls_rule *rule, enum nx_flow_format flow_format)
     }
 }
 
-static uint32_t
-vendor_code_to_id(uint8_t code)
-{
-    switch (code) {
-#define OFPUTIL_VENDOR(NAME, VENDOR_ID) case NAME: return VENDOR_ID;
-        OFPUTIL_VENDORS
-#undef OFPUTIL_VENDOR
-    default:
-        return UINT32_MAX;
-    }
-}
-
-static int
-vendor_id_to_code(uint32_t id)
-{
-    switch (id) {
-#define OFPUTIL_VENDOR(NAME, VENDOR_ID) case VENDOR_ID: return NAME;
-        OFPUTIL_VENDORS
-#undef OFPUTIL_VENDOR
-    default:
-        return -1;
-    }
-}
-
-/* Creates and returns an OpenFlow message of type OFPT_ERROR with the error
- * information taken from 'error', whose encoding must be as described in the
- * large comment in ofp-util.h.  If 'oh' is nonnull, then the error will use
- * oh->xid as its transaction ID, and it will include up to the first 64 bytes
- * of 'oh'.
- *
- * Returns NULL if 'error' is not an OpenFlow error code. */
-struct ofpbuf *
-ofputil_encode_error_msg(int error, const struct ofp_header *oh)
-{
-    static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
-
-    struct ofpbuf *buf;
-    const void *data;
-    size_t len;
-    uint8_t vendor;
-    uint16_t type;
-    uint16_t code;
-    ovs_be32 xid;
-
-    if (!is_ofp_error(error)) {
-        /* We format 'error' with strerror() here since it seems likely to be
-         * a system errno value. */
-        VLOG_WARN_RL(&rl, "invalid OpenFlow error code %d (%s)",
-                     error, strerror(error));
-        return NULL;
-    }
-
-    if (oh) {
-        xid = oh->xid;
-        data = oh;
-        len = ntohs(oh->length);
-        if (len > 64) {
-            len = 64;
-        }
-    } else {
-        xid = 0;
-        data = NULL;
-        len = 0;
-    }
-
-    vendor = get_ofp_err_vendor(error);
-    type = get_ofp_err_type(error);
-    code = get_ofp_err_code(error);
-    if (vendor == OFPUTIL_VENDOR_OPENFLOW) {
-        struct ofp_error_msg *oem;
-
-        oem = make_openflow_xid(len + sizeof *oem, OFPT_ERROR, xid, &buf);
-        oem->type = htons(type);
-        oem->code = htons(code);
-    } else {
-        struct ofp_error_msg *oem;
-        struct nx_vendor_error *nve;
-        uint32_t vendor_id;
-
-        vendor_id = vendor_code_to_id(vendor);
-        if (vendor_id == UINT32_MAX) {
-            VLOG_WARN_RL(&rl, "error %x contains invalid vendor code %d",
-                         error, vendor);
-            return NULL;
-        }
-
-        oem = make_openflow_xid(len + sizeof *oem + sizeof *nve,
-                                OFPT_ERROR, xid, &buf);
-        oem->type = htons(NXET_VENDOR);
-        oem->code = htons(NXVC_VENDOR_ERROR);
-
-        nve = (struct nx_vendor_error *)oem->data;
-        nve->vendor = htonl(vendor_id);
-        nve->type = htons(type);
-        nve->code = htons(code);
-    }
-
-    if (len) {
-        buf->size -= len;
-        ofpbuf_put(buf, data, len);
-    }
-
-    return buf;
-}
-
-/* Decodes 'oh', which should be an OpenFlow OFPT_ERROR message, and returns an
- * Open vSwitch internal error code in the format described in the large
- * comment in ofp-util.h.
- *
- * If 'payload_ofs' is nonnull, on success '*payload_ofs' is set to the offset
- * to the payload starting from 'oh' and on failure it is set to 0. */
-int
-ofputil_decode_error_msg(const struct ofp_header *oh, size_t *payload_ofs)
-{
-    static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
-
-    const struct ofp_error_msg *oem;
-    uint16_t type, code;
-    struct ofpbuf b;
-    int vendor;
-
-    if (payload_ofs) {
-        *payload_ofs = 0;
-    }
-    if (oh->type != OFPT_ERROR) {
-        return EPROTO;
-    }
-
-    ofpbuf_use_const(&b, oh, ntohs(oh->length));
-    oem = ofpbuf_try_pull(&b, sizeof *oem);
-    if (!oem) {
-        return EPROTO;
-    }
-
-    type = ntohs(oem->type);
-    code = ntohs(oem->code);
-    if (type == NXET_VENDOR && code == NXVC_VENDOR_ERROR) {
-        const struct nx_vendor_error *nve = ofpbuf_try_pull(&b, sizeof *nve);
-        if (!nve) {
-            return EPROTO;
-        }
-
-        vendor = vendor_id_to_code(ntohl(nve->vendor));
-        if (vendor < 0) {
-            VLOG_WARN_RL(&rl, "error contains unknown vendor ID %#"PRIx32,
-                         ntohl(nve->vendor));
-            return EPROTO;
-        }
-        type = ntohs(nve->type);
-        code = ntohs(nve->code);
-    } else {
-        vendor = OFPUTIL_VENDOR_OPENFLOW;
-    }
-
-    if (type >= 1024) {
-        VLOG_WARN_RL(&rl, "error contains type %"PRIu16" greater than "
-                     "supported maximum value 1023", type);
-        return EPROTO;
-    }
-
-    if (payload_ofs) {
-        *payload_ofs = (uint8_t *) b.data - (uint8_t *) oh;
-    }
-    return ofp_mkerr_vendor(vendor, type, code);
-}
-
-void
-ofputil_format_error(struct ds *s, int error)
-{
-    if (is_errno(error)) {
-        ds_put_cstr(s, strerror(error));
-    } else {
-        uint16_t type = get_ofp_err_type(error);
-        uint16_t code = get_ofp_err_code(error);
-        const char *type_s = ofp_error_type_to_string(type);
-        const char *code_s = ofp_error_code_to_string(type, code);
-
-        ds_put_format(s, "type ");
-        if (type_s) {
-            ds_put_cstr(s, type_s);
-        } else {
-            ds_put_format(s, "%"PRIu16, type);
-        }
-
-        ds_put_cstr(s, ", code ");
-        if (code_s) {
-            ds_put_cstr(s, code_s);
-        } else {
-            ds_put_format(s, "%"PRIu16, code);
-        }
-    }
-}
-
-char *
-ofputil_error_to_string(int error)
-{
-    struct ds s = DS_EMPTY_INITIALIZER;
-    ofputil_format_error(&s, error);
-    return ds_steal_cstr(&s);
-}
-
 /* Attempts to pull 'actions_len' bytes from the front of 'b'.  Returns 0 if
  * successful, otherwise an OpenFlow error.
  *
@@ -2825,7 +2616,7 @@ ofputil_error_to_string(int error)
  * do so, with validate_actions()).  The caller is also responsible for making
  * sure that 'b->data' is initially aligned appropriately for "union
  * ofp_action". */
-int
+enum ofperr
 ofputil_pull_actions(struct ofpbuf *b, unsigned int actions_len,
                      union ofp_action **actionsp, size_t *n_actionsp)
 {
@@ -2849,7 +2640,7 @@ ofputil_pull_actions(struct ofpbuf *b, unsigned int actions_len,
 error:
     *actionsp = NULL;
     *n_actionsp = 0;
-    return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_LEN);
+    return OFPERR_OFPBRC_BAD_LEN;
 }
 
 bool
diff --git a/lib/ofp-util.h b/lib/ofp-util.h
index 909467f..88e998e 100644
--- a/lib/ofp-util.h
+++ b/lib/ofp-util.h
@@ -88,15 +88,16 @@ enum ofputil_msg_code {
 };
 
 struct ofputil_msg_type;
-int ofputil_decode_msg_type(const struct ofp_header *,
-                            const struct ofputil_msg_type **);
-int ofputil_decode_msg_type_partial(const struct ofp_header *, size_t length,
+enum ofperr ofputil_decode_msg_type(const struct ofp_header *,
                                     const struct ofputil_msg_type **);
+enum ofperr ofputil_decode_msg_type_partial(const struct ofp_header *,
+                                            size_t length,
+                                            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 *);
 
 /* Port numbers. */
-int ofputil_check_output_port(uint16_t ofp_port, int max_ports);
+enum ofperr ofputil_check_output_port(uint16_t ofp_port, int max_ports);
 bool ofputil_port_from_string(const char *, uint16_t *port);
 void ofputil_format_port(uint16_t port, struct ds *);
 
@@ -142,8 +143,9 @@ struct ofputil_flow_mod {
     size_t n_actions;
 };
 
-int ofputil_decode_flow_mod(struct ofputil_flow_mod *,
-                            const struct ofp_header *, bool flow_mod_table_id);
+enum ofperr ofputil_decode_flow_mod(struct ofputil_flow_mod *,
+                                    const struct ofp_header *,
+                                    bool flow_mod_table_id);
 struct ofpbuf *ofputil_encode_flow_mod(const struct ofputil_flow_mod *,
                                        enum nx_flow_format,
                                        bool flow_mod_table_id);
@@ -156,8 +158,8 @@ struct ofputil_flow_stats_request {
     uint8_t table_id;
 };
 
-int ofputil_decode_flow_stats_request(struct ofputil_flow_stats_request *,
-                                      const struct ofp_header *);
+enum ofperr ofputil_decode_flow_stats_request(
+    struct ofputil_flow_stats_request *, const struct ofp_header *);
 struct ofpbuf *ofputil_encode_flow_stats_request(
     const struct ofputil_flow_stats_request *, enum nx_flow_format);
 
@@ -204,8 +206,8 @@ struct ofputil_flow_removed {
     uint64_t byte_count;        /* Byte count, UINT64_MAX if unknown. */
 };
 
-int ofputil_decode_flow_removed(struct ofputil_flow_removed *,
-                                const struct ofp_header *);
+enum ofperr ofputil_decode_flow_removed(struct ofputil_flow_removed *,
+                                        const struct ofp_header *);
 struct ofpbuf *ofputil_encode_flow_removed(const struct ofputil_flow_removed *,
                                            enum nx_flow_format);
 
@@ -397,160 +399,16 @@ ofputil_action_is_valid(const union ofp_action *a, size_t n_actions)
          ((LEFT) -= ntohs((ITER)->header.len) / sizeof(union ofp_action), \
           (ITER) = ofputil_action_next(ITER)))
 
-int validate_actions(const union ofp_action *, size_t n_actions,
-                     const struct flow *, int max_ports);
+enum ofperr validate_actions(const union ofp_action *, size_t n_actions,
+                             const struct flow *, int max_ports);
 bool action_outputs_to_port(const union ofp_action *, ovs_be16 port);
 
-int ofputil_pull_actions(struct ofpbuf *, unsigned int actions_len,
-                         union ofp_action **, size_t *);
+enum ofperr ofputil_pull_actions(struct ofpbuf *, unsigned int actions_len,
+                                 union ofp_action **, size_t *);
 
 bool ofputil_actions_equal(const union ofp_action *a, size_t n_a,
                            const union ofp_action *b, size_t n_b);
 union ofp_action *ofputil_actions_clone(const union ofp_action *, size_t n);
-
-/* OpenFlow vendors.
- *
- * These functions map OpenFlow 32-bit vendor IDs (as used in struct
- * ofp_vendor_header) into 4-bit values to embed in an "int".  The 4-bit values
- * are only used internally in Open vSwitch and never appear on the wire, so
- * particular codes used are not important.
- */
-
-/* Vendor error numbers currently used in Open vSwitch. */
-#define OFPUTIL_VENDORS                                     \
-    /*             vendor name              vendor value */ \
-    OFPUTIL_VENDOR(OFPUTIL_VENDOR_OPENFLOW, 0x00000000)     \
-    OFPUTIL_VENDOR(OFPUTIL_VENDOR_NICIRA,   NX_VENDOR_ID)
-
-/* OFPUTIL_VENDOR_* definitions. */
-enum ofputil_vendor_codes {
-#define OFPUTIL_VENDOR(NAME, VENDOR_ID) NAME,
-    OFPUTIL_VENDORS
-    OFPUTIL_N_VENDORS
-#undef OFPUTIL_VENDOR
-};
-
-/* Error codes.
- *
- * We embed system errno values and OpenFlow standard and vendor extension
- * error codes into a single 31-bit space using the following encoding.
- * (Bit 31 is unused and assumed 0 to avoid negative "int" values.)
- *
- *   30                                                   0
- *  +------------------------------------------------------+
- *  |                           0                          |  success
- *  +------------------------------------------------------+
- *
- *   30 29                                                0
- *  +--+---------------------------------------------------+
- *  | 0|                    errno value                    |  errno value
- *  +--+---------------------------------------------------+
- *
- *   30 29   26 25            16 15                       0
- *  +--+-------+----------------+--------------------------+
- *  | 1|   0   |      type      |           code           |  standard OpenFlow
- *  +--+-------+----------------+--------------------------+  error
- *
- *   30 29   26 25            16 15                       0
- *  +--+-------+----------------+--------------------------+  Nicira
- *  | 1| vendor|      type      |           code           |  NXET_VENDOR
- *  +--+-------+----------------+--------------------------+  error extension
- *
- * C and POSIX say that errno values are positive.  We assume that they are
- * less than 2**29.  They are actually less than 65536 on at least Linux,
- * FreeBSD, OpenBSD, and Windows.
- *
- * The 'vendor' field holds one of the OFPUTIL_VENDOR_* codes defined above.
- * It must be nonzero.
- *
- * Negative values are not defined.
- */
-
-/* Currently 4 bits are allocated to the "vendor" field.  Make sure that all
- * the vendor codes can fit. */
-BUILD_ASSERT_DECL(OFPUTIL_N_VENDORS <= 16);
-
-/* These are macro versions of the functions defined below.  The macro versions
- * are intended for use in contexts where function calls are not allowed,
- * e.g. static initializers and case labels. */
-#define OFP_MKERR(TYPE, CODE) ((1 << 30) | ((TYPE) << 16) | (CODE))
-#define OFP_MKERR_VENDOR(VENDOR, TYPE, CODE) \
-        ((1 << 30) | ((VENDOR) << 26) | ((TYPE) << 16) | (CODE))
-#define OFP_MKERR_NICIRA(TYPE, CODE) \
-        OFP_MKERR_VENDOR(OFPUTIL_VENDOR_NICIRA, TYPE, CODE)
-
-/* Returns the standard OpenFlow error with the specified 'type' and 'code' as
- * an integer. */
-static inline int
-ofp_mkerr(uint16_t type, uint16_t code)
-{
-    return OFP_MKERR(type, code);
-}
-
-/* Returns the OpenFlow vendor error with the specified 'vendor', 'type', and
- * 'code' as an integer.  'vendor' must be an OFPUTIL_VENDOR_* constant. */
-static inline int
-ofp_mkerr_vendor(uint8_t vendor, uint16_t type, uint16_t code)
-{
-    assert(vendor < OFPUTIL_N_VENDORS);
-    return OFP_MKERR_VENDOR(vendor, type, code);
-}
-
-/* Returns the OpenFlow vendor error with Nicira as vendor, with the specific
- * 'type' and 'code', as an integer. */
-static inline int
-ofp_mkerr_nicira(uint16_t type, uint16_t code)
-{
-    return OFP_MKERR_NICIRA(type, code);
-}
-
-/* Returns true if 'error' encodes an OpenFlow standard or vendor extension
- * error codes as documented above. */
-static inline bool
-is_ofp_error(int error)
-{
-    return (error & (1 << 30)) != 0;
-}
-
-/* Returns true if 'error' appears to be a system errno value. */
-static inline bool
-is_errno(int error)
-{
-    return !is_ofp_error(error);
-}
-
-/* Returns the "vendor" part of the OpenFlow error code 'error' (which must be
- * in the format explained above).  This is normally one of the
- * OFPUTIL_VENDOR_* constants.  Returns OFPUTIL_VENDOR_OPENFLOW (0) for a
- * standard OpenFlow error. */
-static inline uint8_t
-get_ofp_err_vendor(int error)
-{
-    return (error >> 26) & 0xf;
-}
-
-/* Returns the "type" part of the OpenFlow error code 'error' (which must be in
- * the format explained above). */
-static inline uint16_t
-get_ofp_err_type(int error)
-{
-    return (error >> 16) & 0x3ff;
-}
-
-/* Returns the "code" part of the OpenFlow error code 'error' (which must be in
- * the format explained above). */
-static inline uint16_t
-get_ofp_err_code(int error)
-{
-    return error & 0xffff;
-}
-
-struct ofpbuf *ofputil_encode_error_msg(int error, const struct ofp_header *);
-int ofputil_decode_error_msg(const struct ofp_header *, size_t *payload_ofs);
-
-/* String versions of errors. */
-void ofputil_format_error(struct ds *, int error);
-char *ofputil_error_to_string(int error);
 
 /* Handy utility for parsing flows and actions. */
 bool ofputil_parse_key_value(char **stringp, char **keyp, char **valuep);
diff --git a/lib/vconn.c b/lib/vconn.c
index 6ea9366..8e6374e 100644
--- a/lib/vconn.c
+++ b/lib/vconn.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2010 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2011 Nicira Networks.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -27,6 +27,7 @@
 #include "dynamic-string.h"
 #include "fatal-signal.h"
 #include "flow.h"
+#include "ofp-errors.h"
 #include "ofp-print.h"
 #include "ofp-util.h"
 #include "ofpbuf.h"
@@ -442,7 +443,6 @@ vcs_recv_hello(struct vconn *vconn)
 static void
 vcs_send_error(struct vconn *vconn)
 {
-    struct ofp_error_msg *error;
     struct ofpbuf *b;
     char s[128];
     int retval;
@@ -450,11 +450,8 @@ vcs_send_error(struct vconn *vconn)
     snprintf(s, sizeof s, "We support versions 0x%02x to 0x%02x inclusive but "
              "you support no later than version 0x%02"PRIx8".",
              vconn->min_version, OFP_VERSION, vconn->version);
-    error = make_openflow(sizeof *error, OFPT_ERROR, &b);
-    error->type = htons(OFPET_HELLO_FAILED);
-    error->code = htons(OFPHFC_INCOMPATIBLE);
-    ofpbuf_put(b, s, strlen(s));
-    update_openflow_length(b);
+    b = ofperr_encode_hello(OFPERR_OFPHFC_INCOMPATIBLE,
+                            ofperr_domain_from_version(vconn->version), s);
     retval = do_send(vconn, b);
     if (retval) {
         ofpbuf_delete(b);
diff --git a/ofproto/connmgr.c b/ofproto/connmgr.c
index 6432ba6..63cb133 100644
--- a/ofproto/connmgr.c
+++ b/ofproto/connmgr.c
@@ -821,24 +821,22 @@ ofconn_send_replies(const struct ofconn *ofconn, struct list *replies)
     }
 }
 
-/* Sends 'error', which should be an OpenFlow error created with
- * e.g. ofp_mkerr(), on 'ofconn', as a reply to 'request'.  Only at most the
+/* Sends 'error' on 'ofconn', as a reply to 'request'.  Only at most the
  * first 64 bytes of 'request' are used. */
 void
 ofconn_send_error(const struct ofconn *ofconn,
-                  const struct ofp_header *request, int error)
+                  const struct ofp_header *request, enum ofperr error)
 {
-    struct ofpbuf *msg;
+    struct ofpbuf *reply;
 
-    msg = ofputil_encode_error_msg(error, request);
-    if (msg) {
+    reply = ofperr_encode_reply(error, request);
+    if (reply) {
         static struct vlog_rate_limit err_rl = VLOG_RATE_LIMIT_INIT(10, 10);
 
         if (!VLOG_DROP_INFO(&err_rl)) {
             const struct ofputil_msg_type *type;
             const char *type_name;
             size_t request_len;
-            char *error_s;
 
             request_len = ntohs(request->length);
             type_name = (!ofputil_decode_msg_type_partial(request,
@@ -847,17 +845,16 @@ ofconn_send_error(const struct ofconn *ofconn,
                          ? ofputil_msg_type_name(type)
                          : "invalid");
 
-            error_s = ofputil_error_to_string(error);
             VLOG_INFO("%s: sending %s error reply to %s message",
-                      rconn_get_name(ofconn->rconn), error_s, type_name);
-            free(error_s);
+                      rconn_get_name(ofconn->rconn), ofperr_to_string(error),
+                      type_name);
         }
-        ofconn_send_reply(ofconn, msg);
+        ofconn_send_reply(ofconn, reply);
     }
 }
 
 /* Same as pktbuf_retrieve(), using the pktbuf owned by 'ofconn'. */
-int
+enum ofperr
 ofconn_pktbuf_retrieve(struct ofconn *ofconn, uint32_t id,
                        struct ofpbuf **bufferp, uint16_t *in_port)
 {
diff --git a/ofproto/connmgr.h b/ofproto/connmgr.h
index 0224352..0040c98 100644
--- a/ofproto/connmgr.h
+++ b/ofproto/connmgr.h
@@ -19,6 +19,7 @@
 
 #include "hmap.h"
 #include "list.h"
+#include "ofp-errors.h"
 #include "ofproto.h"
 #include "openflow/nicira-ext.h"
 #include "openvswitch/types.h"
@@ -94,10 +95,10 @@ void ofconn_set_miss_send_len(struct ofconn *, int miss_send_len);
 void ofconn_send_reply(const struct ofconn *, struct ofpbuf *);
 void ofconn_send_replies(const struct ofconn *, struct list *);
 void ofconn_send_error(const struct ofconn *, const struct ofp_header *request,
-                       int error);
+                       enum ofperr);
 
-int ofconn_pktbuf_retrieve(struct ofconn *, uint32_t id,
-                           struct ofpbuf **bufferp, uint16_t *in_port);
+enum ofperr ofconn_pktbuf_retrieve(struct ofconn *, uint32_t id,
+                                   struct ofpbuf **bufferp, uint16_t *in_port);
 
 bool ofconn_has_pending_opgroups(const struct ofconn *);
 void ofconn_add_opgroup(struct ofconn *, struct list *);
diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c
index bca9b8d..23d5e1a 100644
--- a/ofproto/ofproto-dpif.c
+++ b/ofproto/ofproto-dpif.c
@@ -3762,14 +3762,14 @@ rule_dealloc(struct rule *rule_)
     free(rule);
 }
 
-static int
+static enum ofperr
 rule_construct(struct rule *rule_)
 {
     struct rule_dpif *rule = rule_dpif_cast(rule_);
     struct ofproto_dpif *ofproto = ofproto_dpif_cast(rule->up.ofproto);
     struct rule_dpif *victim;
     uint8_t table_id;
-    int error;
+    enum ofperr error;
 
     error = validate_actions(rule->up.actions, rule->up.n_actions,
                              &rule->up.cr.flow, ofproto->max_ports);
@@ -3846,7 +3846,7 @@ rule_get_stats(struct rule *rule_, uint64_t *packets, uint64_t *bytes)
     }
 }
 
-static int
+static enum ofperr
 rule_execute(struct rule *rule_, const struct flow *flow,
              struct ofpbuf *packet)
 {
@@ -3876,7 +3876,7 @@ rule_modify_actions(struct rule *rule_)
 {
     struct rule_dpif *rule = rule_dpif_cast(rule_);
     struct ofproto_dpif *ofproto = ofproto_dpif_cast(rule->up.ofproto);
-    int error;
+    enum ofperr error;
 
     error = validate_actions(rule->up.actions, rule->up.n_actions,
                              &rule->up.cr.flow, ofproto->max_ports);
@@ -4373,9 +4373,8 @@ xlate_learn_action(struct action_xlate_ctx *ctx,
 
     error = ofproto_flow_mod(&ctx->ofproto->up, &fm);
     if (error && !VLOG_DROP_WARN(&rl)) {
-        char *msg = ofputil_error_to_string(error);
-        VLOG_WARN("learning action failed to modify flow table (%s)", msg);
-        free(msg);
+        VLOG_WARN("learning action failed to modify flow table (%s)",
+                  ofperr_get_name(error));
     }
 
     free(fm.actions);
@@ -5348,16 +5347,16 @@ set_frag_handling(struct ofproto *ofproto_,
     }
 }
 
-static int
+static enum ofperr
 packet_out(struct ofproto *ofproto_, struct ofpbuf *packet,
            const struct flow *flow,
            const union ofp_action *ofp_actions, size_t n_ofp_actions)
 {
     struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
-    int error;
+    enum ofperr error;
 
     if (flow->in_port >= ofproto->max_ports && flow->in_port < OFPP_MAX) {
-        return ofp_mkerr_nicira(OFPET_BAD_REQUEST, NXBRC_BAD_IN_PORT);
+        return OFPERR_NXBRC_BAD_IN_PORT;
     }
 
     error = validate_actions(ofp_actions, n_ofp_actions, flow,
diff --git a/ofproto/ofproto-provider.h b/ofproto/ofproto-provider.h
index 6576069..554affc 100644
--- a/ofproto/ofproto-provider.h
+++ b/ofproto/ofproto-provider.h
@@ -23,6 +23,7 @@
 #include "cfm.h"
 #include "classifier.h"
 #include "list.h"
+#include "ofp-errors.h"
 #include "shash.h"
 #include "timeval.h"
 
@@ -140,7 +141,7 @@ rule_from_cls_rule(const struct cls_rule *cls_rule)
 void ofproto_rule_expire(struct rule *, uint8_t reason);
 void ofproto_rule_destroy(struct rule *);
 
-void ofoperation_complete(struct ofoperation *, int status);
+void ofoperation_complete(struct ofoperation *, enum ofperr);
 struct rule *ofoperation_get_victim(struct ofoperation *);
 
 /* ofproto class structure, to be defined by each ofproto implementation.
@@ -235,7 +236,7 @@ struct rule *ofoperation_get_victim(struct ofoperation *);
  *
  * Most of these functions return 0 if they are successful or a positive error
  * code on failure.  Depending on the function, valid error codes are either
- * errno values or OpenFlow error codes constructed with ofp_mkerr().
+ * errno values or OFPERR_* OpenFlow error codes.
  *
  * Most of these functions are expected to execute synchronously, that is, to
  * block as necessary to obtain a result.  Thus, these functions may return
@@ -620,7 +621,7 @@ struct ofproto_class {
 
     /* Chooses an appropriate table for 'cls_rule' within 'ofproto'.  On
      * success, stores the table ID into '*table_idp' and returns 0.  On
-     * failure, returns an OpenFlow error code (as returned by ofp_mkerr()).
+     * failure, returns an OpenFlow error code.
      *
      * The choice of table should be a function of 'cls_rule' and 'ofproto''s
      * datapath capabilities.  It should not depend on the flows already in
@@ -632,9 +633,9 @@ struct ofproto_class {
      * should choose one arbitrarily (but deterministically).
      *
      * If this function is NULL then table 0 is always chosen. */
-    int (*rule_choose_table)(const struct ofproto *ofproto,
-                             const struct cls_rule *cls_rule,
-                             uint8_t *table_idp);
+    enum ofperr (*rule_choose_table)(const struct ofproto *ofproto,
+                                     const struct cls_rule *cls_rule,
+                                     uint8_t *table_idp);
 
     /* Life-cycle functions for a "struct rule" (see "Life Cycle" above).
      *
@@ -745,8 +746,8 @@ struct ofproto_class {
      *
      *       * Call ofoperation_complete() and return 0.
      *
-     *       * Return an OpenFlow error code (as returned by ofp_mkerr()).  (Do
-     *         not call ofoperation_complete() in this case.)
+     *       * Return an OpenFlow error code.  (Do not call
+     *         ofoperation_complete() in this case.)
      *
      *     Either way, ->rule_destruct() will not be called for 'rule', but
      *     ->rule_dealloc() will be.
@@ -771,7 +772,7 @@ struct ofproto_class {
      *
      * Rule destruction must not fail. */
     struct rule *(*rule_alloc)(void);
-    int (*rule_construct)(struct rule *rule);
+    enum ofperr (*rule_construct)(struct rule *rule);
     void (*rule_destruct)(struct rule *rule);
     void (*rule_dealloc)(struct rule *rule);
 
@@ -795,10 +796,9 @@ struct ofproto_class {
      *
      * The statistics for 'packet' should be included in 'rule'.
      *
-     * Returns 0 if successful, otherwise an OpenFlow error code (as returned
-     * by ofp_mkerr()). */
-    int (*rule_execute)(struct rule *rule, const struct flow *flow,
-                        struct ofpbuf *packet);
+     * Returns 0 if successful, otherwise an OpenFlow error code. */
+    enum ofperr (*rule_execute)(struct rule *rule, const struct flow *flow,
+                                struct ofpbuf *packet);
 
     /* When ->rule_modify_actions() is called, the caller has already replaced
      * the OpenFlow actions in 'rule' by a new set.  (The original actions are
@@ -862,8 +862,7 @@ struct ofproto_class {
      *
      * This function must validate that the 'n_actions' elements in 'actions'
      * are well-formed OpenFlow actions that can be correctly implemented by
-     * the datapath.  If not, then it should return an OpenFlow error code (as
-     * returned by ofp_mkerr()).
+     * the datapath.  If not, then it should return an OpenFlow error code.
      *
      * 'flow' reflects the flow information for 'packet'.  All of the
      * information in 'flow' is extracted from 'packet', except for
@@ -873,12 +872,11 @@ struct ofproto_class {
      * 'packet' is not matched against the OpenFlow flow table, so its
      * statistics should not be included in OpenFlow flow statistics.
      *
-     * Returns 0 if successful, otherwise an OpenFlow error code (as returned
-     * by ofp_mkerr()). */
-    int (*packet_out)(struct ofproto *ofproto, struct ofpbuf *packet,
-                      const struct flow *flow,
-                      const union ofp_action *actions,
-                      size_t n_actions);
+     * Returns 0 if successful, otherwise an OpenFlow error code. */
+    enum ofperr (*packet_out)(struct ofproto *ofproto, struct ofpbuf *packet,
+                              const struct flow *flow,
+                              const union ofp_action *actions,
+                              size_t n_actions);
 
 /* ## ------------------------- ## */
 /* ## OFPP_NORMAL configuration ## */
@@ -1091,10 +1089,11 @@ int ofproto_class_unregister(const struct ofproto_class *);
  *
  * ofproto.c also uses this value internally for additional (similar) purposes.
  *
- * This particular value is a good choice because it is negative (so it won't
- * collide with any errno value or any value returned by ofp_mkerr()) and large
- * (so it won't accidentally collide with EOF or a negative errno value). */
-enum { OFPROTO_POSTPONE = -100000 };
+ * This particular value is a good choice because it is large, so that it does
+ * not collide with any errno value, but not large enough to collide with an
+ * OFPERR_* value. */
+enum { OFPROTO_POSTPONE = 1 << 16 };
+BUILD_ASSERT_DECL(OFPROTO_POSTPONE < OFPERR_OFS);
 
 int ofproto_flow_mod(struct ofproto *, const struct ofputil_flow_mod *);
 void ofproto_add_flow(struct ofproto *, const struct cls_rule *,
diff --git a/ofproto/ofproto.c b/ofproto/ofproto.c
index 810fe12..fc53bf2 100644
--- a/ofproto/ofproto.c
+++ b/ofproto/ofproto.c
@@ -31,6 +31,7 @@
 #include "hmap.h"
 #include "netdev.h"
 #include "nx-match.h"
+#include "ofp-errors.h"
 #include "ofp-print.h"
 #include "ofp-util.h"
 #include "ofpbuf.h"
@@ -136,13 +137,13 @@ static void ofproto_rule_send_removed(struct rule *, uint8_t reason);
 
 static void ofopgroup_destroy(struct ofopgroup *);
 
-static int add_flow(struct ofproto *, struct ofconn *,
-                    const struct ofputil_flow_mod *,
-                    const struct ofp_header *);
+static enum ofperr add_flow(struct ofproto *, struct ofconn *,
+                            const struct ofputil_flow_mod *,
+                            const struct ofp_header *);
 
 static bool handle_openflow(struct ofconn *, struct ofpbuf *);
-static int handle_flow_mod__(struct ofproto *, struct ofconn *,
-                             const struct ofputil_flow_mod *,
+static enum ofperr handle_flow_mod__(struct ofproto *, struct ofconn *,
+                                     const struct ofputil_flow_mod *,
                              const struct ofp_header *);
 
 static void update_port(struct ofproto *, const char *devname);
@@ -1191,9 +1192,8 @@ ofproto_add_flow(struct ofproto *ofproto, const struct cls_rule *cls_rule,
 }
 
 /* Executes the flow modification specified in 'fm'.  Returns 0 on success, an
- * OpenFlow error code as encoded by ofp_mkerr() on failure, or
- * OFPROTO_POSTPONE if the operation cannot be initiated now but may be retried
- * later.
+ * OFPERR_* OpenFlow error code on failure, or OFPROTO_POSTPONE if the
+ * operation cannot be initiated now but may be retried later.
  *
  * This is a helper function for in-band control and fail-open. */
 int
@@ -1687,14 +1687,14 @@ rule_is_hidden(const struct rule *rule)
     return rule->cr.priority > UINT16_MAX;
 }
 
-static int
+static enum ofperr
 handle_echo_request(struct ofconn *ofconn, const struct ofp_header *oh)
 {
     ofconn_send_reply(ofconn, make_echo_reply(oh));
     return 0;
 }
 
-static int
+static enum ofperr
 handle_features_request(struct ofconn *ofconn, const struct ofp_header *oh)
 {
     struct ofproto *ofproto = ofconn_get_ofproto(ofconn);
@@ -1726,7 +1726,7 @@ handle_features_request(struct ofconn *ofconn, const struct ofp_header *oh)
     return 0;
 }
 
-static int
+static enum ofperr
 handle_get_config_request(struct ofconn *ofconn, const struct ofp_header *oh)
 {
     struct ofproto *ofproto = ofconn_get_ofproto(ofconn);
@@ -1742,7 +1742,7 @@ handle_get_config_request(struct ofconn *ofconn, const struct ofp_header *oh)
     return 0;
 }
 
-static int
+static enum ofperr
 handle_set_config(struct ofconn *ofconn, const struct ofp_switch_config *osc)
 {
     struct ofproto *ofproto = ofconn_get_ofproto(ofconn);
@@ -1771,20 +1771,22 @@ handle_set_config(struct ofconn *ofconn, const struct ofp_switch_config *osc)
 }
 
 /* Checks whether 'ofconn' is a slave controller.  If so, returns an OpenFlow
- * error message code (composed with ofp_mkerr()) for the caller to propagate
- * upward.  Otherwise, returns 0. */
-static int
-reject_slave_controller(const struct ofconn *ofconn)
+ * error message code for the caller to propagate upward.  Otherwise, returns
+ * 0.
+ *
+ * The log message mentions 'msg_type'. */
+static enum ofperr
+reject_slave_controller(struct ofconn *ofconn)
 {
     if (ofconn_get_type(ofconn) == OFCONN_PRIMARY
         && ofconn_get_role(ofconn) == NX_ROLE_SLAVE) {
-        return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_EPERM);
+        return OFPERR_OFPBRC_EPERM;
     } else {
         return 0;
     }
 }
 
-static int
+static enum ofperr
 handle_packet_out(struct ofconn *ofconn, const struct ofp_header *oh)
 {
     struct ofproto *p = ofconn_get_ofproto(ofconn);
@@ -1794,8 +1796,8 @@ handle_packet_out(struct ofconn *ofconn, const struct ofp_header *oh)
     struct ofpbuf request;
     struct flow flow;
     size_t n_ofp_actions;
+    enum ofperr error;
     uint16_t in_port;
-    int error;
 
     COVERAGE_INC(ofproto_packet_out);
 
@@ -1835,7 +1837,7 @@ handle_packet_out(struct ofconn *ofconn, const struct ofp_header *oh)
      * above) are valid. */
     in_port = ntohs(opo->in_port);
     if (in_port >= OFPP_MAX && in_port != OFPP_LOCAL && in_port != OFPP_NONE) {
-        return ofp_mkerr_nicira(OFPET_BAD_REQUEST, NXBRC_BAD_IN_PORT);
+        return OFPERR_NXBRC_BAD_IN_PORT;
     }
 
     /* Send out packet. */
@@ -1869,7 +1871,7 @@ update_port_config(struct ofport *port, ovs_be32 config, ovs_be32 mask)
     }
 }
 
-static int
+static enum ofperr
 handle_port_mod(struct ofconn *ofconn, const struct ofp_header *oh)
 {
     struct ofproto *p = ofconn_get_ofproto(ofconn);
@@ -1884,9 +1886,9 @@ handle_port_mod(struct ofconn *ofconn, const struct ofp_header *oh)
 
     port = ofproto_get_port(p, ntohs(opm->port_no));
     if (!port) {
-        return ofp_mkerr(OFPET_PORT_MOD_FAILED, OFPPMFC_BAD_PORT);
+        return OFPERR_OFPPMFC_BAD_PORT;
     } else if (memcmp(port->opp.hw_addr, opm->hw_addr, OFP_ETH_ALEN)) {
-        return ofp_mkerr(OFPET_PORT_MOD_FAILED, OFPPMFC_BAD_HW_ADDR);
+        return OFPERR_OFPPMFC_BAD_HW_ADDR;
     } else {
         update_port_config(port, opm->config, opm->mask);
         if (opm->advertise) {
@@ -1896,7 +1898,7 @@ handle_port_mod(struct ofconn *ofconn, const struct ofp_header *oh)
     return 0;
 }
 
-static int
+static enum ofperr
 handle_desc_stats_request(struct ofconn *ofconn,
                           const struct ofp_stats_msg *request)
 {
@@ -1915,7 +1917,7 @@ handle_desc_stats_request(struct ofconn *ofconn,
     return 0;
 }
 
-static int
+static enum ofperr
 handle_table_stats_request(struct ofconn *ofconn,
                            const struct ofp_stats_msg *request)
 {
@@ -1969,7 +1971,7 @@ append_port_stat(struct ofport *port, struct list *replies)
     put_32aligned_be64(&ops->collisions, htonll(stats.collisions));
 }
 
-static int
+static enum ofperr
 handle_port_stats_request(struct ofconn *ofconn,
                           const struct ofp_port_stats_request *psr)
 {
@@ -2003,12 +2005,12 @@ calc_flow_duration__(long long int start, uint32_t *sec, uint32_t *nsec)
 
 /* Checks whether 'table_id' is 0xff or a valid table ID in 'ofproto'.  Returns
  * 0 if 'table_id' is OK, otherwise an OpenFlow error code.  */
-static int
+static enum ofperr
 check_table_id(const struct ofproto *ofproto, uint8_t table_id)
 {
     return (table_id == 0xff || table_id < ofproto->n_tables
             ? 0
-            : ofp_mkerr_nicira(OFPET_BAD_REQUEST, NXBRC_BAD_TABLE_ID));
+            : OFPERR_NXBRC_BAD_TABLE_ID);
 
 }
 
@@ -2064,13 +2066,13 @@ next_matching_table(struct ofproto *ofproto,
  * Hidden rules are always omitted.
  *
  * Returns 0 on success, otherwise an OpenFlow error code. */
-static int
+static enum ofperr
 collect_rules_loose(struct ofproto *ofproto, uint8_t table_id,
                     const struct cls_rule *match, uint16_t out_port,
                     struct list *rules)
 {
     struct classifier *cls;
-    int error;
+    enum ofperr error;
 
     error = check_table_id(ofproto, table_id);
     if (error) {
@@ -2106,7 +2108,7 @@ collect_rules_loose(struct ofproto *ofproto, uint8_t table_id,
  * Hidden rules are always omitted.
  *
  * Returns 0 on success, otherwise an OpenFlow error code. */
-static int
+static enum ofperr
 collect_rules_strict(struct ofproto *ofproto, uint8_t table_id,
                      const struct cls_rule *match, uint16_t out_port,
                      struct list *rules)
@@ -2136,7 +2138,7 @@ collect_rules_strict(struct ofproto *ofproto, uint8_t table_id,
     return 0;
 }
 
-static int
+static enum ofperr
 handle_flow_stats_request(struct ofconn *ofconn,
                           const struct ofp_stats_msg *osm)
 {
@@ -2145,7 +2147,7 @@ handle_flow_stats_request(struct ofconn *ofconn,
     struct list replies;
     struct list rules;
     struct rule *rule;
-    int error;
+    enum ofperr error;
 
     error = ofputil_decode_flow_stats_request(&fsr, &osm->header);
     if (error) {
@@ -2264,7 +2266,7 @@ ofproto_port_get_cfm_remote_mpids(const struct ofproto *ofproto,
             : -1);
 }
 
-static int
+static enum ofperr
 handle_aggregate_stats_request(struct ofconn *ofconn,
                                const struct ofp_stats_msg *osm)
 {
@@ -2275,7 +2277,7 @@ handle_aggregate_stats_request(struct ofconn *ofconn,
     struct ofpbuf *reply;
     struct list rules;
     struct rule *rule;
-    int error;
+    enum ofperr error;
 
     error = ofputil_decode_flow_stats_request(&request, &osm->header);
     if (error) {
@@ -2371,7 +2373,7 @@ handle_queue_stats_for_port(struct ofport *port, uint32_t queue_id,
     }
 }
 
-static int
+static enum ofperr
 handle_queue_stats_request(struct ofconn *ofconn,
                            const struct ofp_queue_stats_request *qsr)
 {
@@ -2398,7 +2400,7 @@ handle_queue_stats_request(struct ofconn *ofconn,
         }
     } else {
         ofpbuf_list_delete(&cbdata.replies);
-        return ofp_mkerr(OFPET_QUEUE_OP_FAILED, OFPQOFC_BAD_PORT);
+        return OFPERR_OFPQOFC_BAD_PORT;
     }
     ofconn_send_replies(ofconn, &cbdata.replies);
 
@@ -2430,12 +2432,12 @@ is_flow_deletion_pending(const struct ofproto *ofproto,
  *
  * Adds the flow specified by 'ofm', which is followed by 'n_actions'
  * ofp_actions, to the ofproto's flow table.  Returns 0 on success, an OpenFlow
- * error code as encoded by ofp_mkerr() on failure, or OFPROTO_POSTPONE if the
- * operation cannot be initiated now but may be retried later.
+ * error code on failure, or OFPROTO_POSTPONE if the operation cannot be
+ * initiated now but may be retried later.
  *
  * 'ofconn' is used to retrieve the packet buffer specified in ofm->buffer_id,
  * if any. */
-static int
+static enum ofperr
 add_flow(struct ofproto *ofproto, struct ofconn *ofconn,
          const struct ofputil_flow_mod *fm, const struct ofp_header *request)
 {
@@ -2467,13 +2469,13 @@ add_flow(struct ofproto *ofproto, struct ofconn *ofconn,
     } else if (fm->table_id < ofproto->n_tables) {
         table = &ofproto->tables[fm->table_id];
     } else {
-        return ofp_mkerr_nicira(OFPET_FLOW_MOD_FAILED, NXFMFC_BAD_TABLE_ID);
+        return OFPERR_NXFMFC_BAD_TABLE_ID;
     }
 
     /* Check for overlap, if requested. */
     if (fm->flags & OFPFF_CHECK_OVERLAP
         && classifier_rule_overlaps(table, &fm->cr)) {
-        return ofp_mkerr(OFPET_FLOW_MOD_FAILED, OFPFMFC_OVERLAP);
+        return OFPERR_OFPFMFC_OVERLAP;
     }
 
     /* Serialize against pending deletion. */
@@ -2537,7 +2539,7 @@ add_flow(struct ofproto *ofproto, struct ofconn *ofconn,
  * if any.
  *
  * Returns 0 on success, otherwise an OpenFlow error code. */
-static int
+static enum ofperr
 modify_flows__(struct ofproto *ofproto, struct ofconn *ofconn,
                const struct ofputil_flow_mod *fm,
                const struct ofp_header *request, struct list *rules)
@@ -2565,12 +2567,12 @@ modify_flows__(struct ofproto *ofproto, struct ofconn *ofconn,
     return 0;
 }
 
-/* Implements OFPFC_MODIFY.  Returns 0 on success or an OpenFlow error code as
- * encoded by ofp_mkerr() on failure.
+/* Implements OFPFC_MODIFY.  Returns 0 on success or an OpenFlow error code on
+ * failure.
  *
  * 'ofconn' is used to retrieve the packet buffer specified in fm->buffer_id,
  * if any. */
-static int
+static enum ofperr
 modify_flows_loose(struct ofproto *ofproto, struct ofconn *ofconn,
                    const struct ofputil_flow_mod *fm,
                    const struct ofp_header *request)
@@ -2586,11 +2588,11 @@ modify_flows_loose(struct ofproto *ofproto, struct ofconn *ofconn,
 }
 
 /* Implements OFPFC_MODIFY_STRICT.  Returns 0 on success or an OpenFlow error
- * code as encoded by ofp_mkerr() on failure.
+ * code on failure.
  *
  * 'ofconn' is used to retrieve the packet buffer specified in fm->buffer_id,
  * if any. */
-static int
+static enum ofperr
 modify_flow_strict(struct ofproto *ofproto, struct ofconn *ofconn,
                    const struct ofputil_flow_mod *fm,
                    const struct ofp_header *request)
@@ -2612,7 +2614,7 @@ modify_flow_strict(struct ofproto *ofproto, struct ofconn *ofconn,
 /* Deletes the rules listed in 'rules'.
  *
  * Returns 0 on success, otherwise an OpenFlow error code. */
-static int
+static enum ofperr
 delete_flows__(struct ofproto *ofproto, struct ofconn *ofconn,
                const struct ofp_header *request, struct list *rules)
 {
@@ -2633,13 +2635,13 @@ delete_flows__(struct ofproto *ofproto, struct ofconn *ofconn,
 }
 
 /* Implements OFPFC_DELETE. */
-static int
+static enum ofperr
 delete_flows_loose(struct ofproto *ofproto, struct ofconn *ofconn,
                    const struct ofputil_flow_mod *fm,
                    const struct ofp_header *request)
 {
     struct list rules;
-    int error;
+    enum ofperr error;
 
     error = collect_rules_loose(ofproto, fm->table_id, &fm->cr, fm->out_port,
                                 &rules);
@@ -2650,13 +2652,13 @@ delete_flows_loose(struct ofproto *ofproto, struct ofconn *ofconn,
 }
 
 /* Implements OFPFC_DELETE_STRICT. */
-static int
+static enum ofperr
 delete_flow_strict(struct ofproto *ofproto, struct ofconn *ofconn,
                    const struct ofputil_flow_mod *fm,
                    const struct ofp_header *request)
 {
     struct list rules;
-    int error;
+    enum ofperr error;
 
     error = collect_rules_strict(ofproto, fm->table_id, &fm->cr, fm->out_port,
                                  &rules);
@@ -2709,11 +2711,11 @@ ofproto_rule_expire(struct rule *rule, uint8_t reason)
     ofopgroup_submit(group);
 }
 
-static int
+static enum ofperr
 handle_flow_mod(struct ofconn *ofconn, const struct ofp_header *oh)
 {
     struct ofputil_flow_mod fm;
-    int error;
+    enum ofperr error;
 
     error = reject_slave_controller(ofconn);
     if (error) {
@@ -2731,13 +2733,13 @@ handle_flow_mod(struct ofconn *ofconn, const struct ofp_header *oh)
     if (fm.flags & OFPFF_EMERG) {
         /* There isn't a good fit for an error code, so just state that the
          * flow table is full. */
-        return ofp_mkerr(OFPET_FLOW_MOD_FAILED, OFPFMFC_ALL_TABLES_FULL);
+        return OFPERR_OFPFMFC_ALL_TABLES_FULL;
     }
 
     return handle_flow_mod__(ofconn_get_ofproto(ofconn), ofconn, &fm, oh);
 }
 
-static int
+static enum ofperr
 handle_flow_mod__(struct ofproto *ofproto, struct ofconn *ofconn,
                   const struct ofputil_flow_mod *fm,
                   const struct ofp_header *oh)
@@ -2768,11 +2770,11 @@ handle_flow_mod__(struct ofproto *ofproto, struct ofconn *ofconn,
             VLOG_WARN_RL(&rl, "flow_mod has explicit table_id but "
                          "flow_mod_table_id extension is not enabled");
         }
-        return ofp_mkerr(OFPET_FLOW_MOD_FAILED, OFPFMFC_BAD_COMMAND);
+        return OFPERR_OFPFMFC_BAD_COMMAND;
     }
 }
 
-static int
+static enum ofperr
 handle_role_request(struct ofconn *ofconn, const struct ofp_header *oh)
 {
     struct nx_role_request *nrr = (struct nx_role_request *) oh;
@@ -2781,13 +2783,13 @@ handle_role_request(struct ofconn *ofconn, const struct ofp_header *oh)
     uint32_t role;
 
     if (ofconn_get_type(ofconn) != OFCONN_PRIMARY) {
-        return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_EPERM);
+        return OFPERR_OFPBRC_EPERM;
     }
 
     role = ntohl(nrr->role);
     if (role != NX_ROLE_OTHER && role != NX_ROLE_MASTER
         && role != NX_ROLE_SLAVE) {
-        return ofp_mkerr_nicira(OFPET_BAD_REQUEST, NXBRC_BAD_ROLE);
+        return OFPERR_NXBRC_BAD_ROLE;
     }
 
     if (ofconn_get_role(ofconn) != role
@@ -2804,7 +2806,7 @@ handle_role_request(struct ofconn *ofconn, const struct ofp_header *oh)
     return 0;
 }
 
-static int
+static enum ofperr
 handle_nxt_flow_mod_table_id(struct ofconn *ofconn,
                              const struct ofp_header *oh)
 {
@@ -2815,7 +2817,7 @@ handle_nxt_flow_mod_table_id(struct ofconn *ofconn,
     return 0;
 }
 
-static int
+static enum ofperr
 handle_nxt_set_flow_format(struct ofconn *ofconn, const struct ofp_header *oh)
 {
     const struct nx_set_flow_format *msg
@@ -2824,7 +2826,7 @@ handle_nxt_set_flow_format(struct ofconn *ofconn, const struct ofp_header *oh)
 
     format = ntohl(msg->format);
     if (format != NXFF_OPENFLOW10 && format != NXFF_NXM) {
-        return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_EPERM);
+        return OFPERR_OFPBRC_EPERM;
     }
 
     if (format != ofconn_get_flow_format(ofconn)
@@ -2837,7 +2839,7 @@ handle_nxt_set_flow_format(struct ofconn *ofconn, const struct ofp_header *oh)
     return 0;
 }
 
-static int
+static enum ofperr
 handle_barrier_request(struct ofconn *ofconn, const struct ofp_header *oh)
 {
     struct ofp_header *ob;
@@ -2852,12 +2854,12 @@ handle_barrier_request(struct ofconn *ofconn, const struct ofp_header *oh)
     return 0;
 }
 
-static int
+static enum ofperr
 handle_openflow__(struct ofconn *ofconn, const struct ofpbuf *msg)
 {
     const struct ofp_header *oh = msg->data;
     const struct ofputil_msg_type *type;
-    int error;
+    enum ofperr error;
 
     error = ofputil_decode_msg_type(oh, &type);
     if (error) {
@@ -2951,9 +2953,9 @@ handle_openflow__(struct ofconn *ofconn, const struct ofpbuf *msg)
     case OFPUTIL_NXST_AGGREGATE_REPLY:
     default:
         if (oh->type == OFPT_STATS_REQUEST || oh->type == OFPT_STATS_REPLY) {
-            return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_STAT);
+            return OFPERR_OFPBRC_BAD_STAT;
         } else {
-            return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_TYPE);
+            return OFPERR_OFPBRC_BAD_TYPE;
         }
     }
 }
@@ -3099,8 +3101,7 @@ ofoperation_destroy(struct ofoperation *op)
 }
 
 /* Indicates that 'op' completed with status 'error', which is either 0 to
- * indicate success or an OpenFlow error code (constructed with
- * e.g. ofp_mkerr()).
+ * indicate success or an OpenFlow error code on failure.
  *
  * If 'error' is 0, indicating success, the operation will be committed
  * permanently to the flow table.  There is one interesting subcase:
@@ -3129,7 +3130,7 @@ ofoperation_destroy(struct ofoperation *op)
  * Please see the large comment in ofproto/ofproto-provider.h titled
  * "Asynchronous Operation Support" for more information. */
 void
-ofoperation_complete(struct ofoperation *op, int error)
+ofoperation_complete(struct ofoperation *op, enum ofperr error)
 {
     struct ofopgroup *group = op->group;
     struct rule *rule = op->rule;
diff --git a/ofproto/pktbuf.c b/ofproto/pktbuf.c
index fc4c66c..9382047 100644
--- a/ofproto/pktbuf.c
+++ b/ofproto/pktbuf.c
@@ -152,8 +152,7 @@ pktbuf_get_null(void)
 }
 
 /* Attempts to retrieve a saved packet with the given 'id' from 'pb'.  Returns
- * 0 if successful, otherwise an OpenFlow error code constructed with
- * ofp_mkerr().
+ * 0 if successful, otherwise an OpenFlow error code.
  *
  * On success, ordinarily stores the buffered packet in '*bufferp' and the
  * datapath port number on which the packet was received in '*in_port'.  The
@@ -167,13 +166,13 @@ pktbuf_get_null(void)
  * headroom.
  *
  * On failure, stores NULL in in '*bufferp' and UINT16_MAX in '*in_port'. */
-int
+enum ofperr
 pktbuf_retrieve(struct pktbuf *pb, uint32_t id, struct ofpbuf **bufferp,
                 uint16_t *in_port)
 {
     static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 20);
     struct packet *p;
-    int error;
+    enum ofperr error;
 
     if (id == UINT32_MAX) {
         error = 0;
@@ -183,7 +182,7 @@ pktbuf_retrieve(struct pktbuf *pb, uint32_t id, struct ofpbuf **bufferp,
     if (!pb) {
         VLOG_WARN_RL(&rl, "attempt to send buffered packet via connection "
                      "without buffers");
-        return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BUFFER_UNKNOWN);
+        return OFPERR_OFPBRC_BUFFER_UNKNOWN;
     }
 
     p = &pb->packets[id & PKTBUF_MASK];
@@ -200,13 +199,13 @@ pktbuf_retrieve(struct pktbuf *pb, uint32_t id, struct ofpbuf **bufferp,
         } else {
             COVERAGE_INC(pktbuf_reuse_error);
             VLOG_WARN_RL(&rl, "attempt to reuse buffer %08"PRIx32, id);
-            error = ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BUFFER_EMPTY);
+            error = OFPERR_OFPBRC_BUFFER_EMPTY;
         }
     } else if (id >> PKTBUF_BITS != COOKIE_MAX) {
         COVERAGE_INC(pktbuf_buffer_unknown);
         VLOG_WARN_RL(&rl, "cookie mismatch: %08"PRIx32" != %08"PRIx32,
                      id, (id & PKTBUF_MASK) | (p->cookie << PKTBUF_BITS));
-        error = ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BUFFER_UNKNOWN);
+        error = OFPERR_OFPBRC_BUFFER_UNKNOWN;
     } else {
         COVERAGE_INC(pktbuf_null_cookie);
         VLOG_INFO_RL(&rl, "Received null cookie %08"PRIx32" (this is normal "
diff --git a/ofproto/pktbuf.h b/ofproto/pktbuf.h
index 67f4973..80bf540 100644
--- a/ofproto/pktbuf.h
+++ b/ofproto/pktbuf.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2011 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,6 +19,8 @@
 
 #include <stdint.h>
 
+#include "ofp-errors.h"
+
 struct pktbuf;
 struct ofpbuf;
 
@@ -28,8 +30,8 @@ struct pktbuf *pktbuf_create(void);
 void pktbuf_destroy(struct pktbuf *);
 uint32_t pktbuf_save(struct pktbuf *, struct ofpbuf *buffer, uint16_t in_port);
 uint32_t pktbuf_get_null(void);
-int pktbuf_retrieve(struct pktbuf *, uint32_t id, struct ofpbuf **bufferp,
-                    uint16_t *in_port);
+enum ofperr pktbuf_retrieve(struct pktbuf *, uint32_t id,
+                            struct ofpbuf **bufferp, uint16_t *in_port);
 void pktbuf_discard(struct pktbuf *, uint32_t id);
 
 #endif /* pktbuf.h */
diff --git a/tests/ofp-print.at b/tests/ofp-print.at
index df5dc9b..70d4335 100644
--- a/tests/ofp-print.at
+++ b/tests/ofp-print.at
@@ -58,7 +58,7 @@ AT_CLEANUP
 AT_SETUP([OFPT_ERROR with type OFPET_HELLO_FAILED])
 AT_KEYWORDS([ofp-print])
 AT_CHECK([ovs-ofctl ofp-print 010100170000000000000001657874726120646174610a], [0], [dnl
-OFPT_ERROR (xid=0x0): type OFPET_HELLO_FAILED, code OFPHFC_EPERM
+OFPT_ERROR (xid=0x0): OFPHFC_EPERM
 extra data\012
 ])
 AT_CLEANUP
@@ -66,7 +66,7 @@ AT_CLEANUP
 AT_SETUP([OFPT_ERROR with type OFPET_BAD_REQUEST])
 AT_KEYWORDS([ofp-print])
 AT_CHECK([ovs-ofctl ofp-print 01010014000000000001000601bbccddeeff0011], [0], [dnl
-OFPT_ERROR (xid=0x0): type OFPET_BAD_REQUEST, code OFPBRC_BAD_LEN
+OFPT_ERROR (xid=0x0): OFPBRC_BAD_LEN
 (***truncated to 8 bytes from 52445***)
 00000000  01 bb cc dd ee ff 00 11-                        |........        |
 ])
@@ -75,7 +75,7 @@ AT_CLEANUP
 AT_SETUP([OFPT_ERROR with code NXBRC_NXM_BAD_PREREQ])
 AT_KEYWORDS([ofp-print])
 AT_CHECK([ovs-ofctl ofp-print '0101001c55555555 b0c20000 0000232000010104 0102000811111111'], [0], [dnl
-OFPT_ERROR (xid=0x55555555): type OFPET_BAD_REQUEST, code NXBRC_NXM_BAD_PREREQ
+OFPT_ERROR (xid=0x55555555): NXBRC_NXM_BAD_PREREQ
 OFPT_ECHO_REQUEST (xid=0x11111111): 0 bytes of payload
 ])
 AT_CLEANUP
@@ -180,7 +180,7 @@ ff fe 50 54 00 00 00 01 62 72 30 00 00 00 00 00 \
 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \
 00 00 02 08 00 00 02 8f 00 00 02 8f \
 "], [0], [dnl
-***decode error: type OFPET_BAD_REQUEST, code OFPBRC_BAD_LEN***
+***decode error: OFPBRC_BAD_LEN***
 00000000  01 06 00 dc 00 00 00 01-00 00 50 54 00 00 00 01 |..........PT....|
 00000010  00 00 01 00 02 00 00 00-00 00 00 87 00 00 0f ff |................|
 00000020  ff fe 50 54 00 00 00 01-62 72 30 00 00 00 00 00 |..PT....br0.....|
diff --git a/tests/ovs-ofctl.at b/tests/ovs-ofctl.at
index 2a458c8..acf5809 100644
--- a/tests/ovs-ofctl.at
+++ b/tests/ovs-ofctl.at
@@ -390,7 +390,7 @@ NXM_OF_IN_PORT(0012), NXM_OF_ETH_TYPE(0800)
 
 # vlan tci
 NXM_OF_VLAN_TCI(f009)
-nx_pull_match() returned error 44010105 (type OFPET_BAD_REQUEST, code NXBRC_NXM_DUP_TYPE)
+nx_pull_match() returned error NXBRC_NXM_DUP_TYPE
 NXM_OF_VLAN_TCI(0000)
 NXM_OF_VLAN_TCI(3123)
 NXM_OF_VLAN_TCI(0123)
@@ -400,114 +400,114 @@ NXM_OF_VLAN_TCI_W(0000/e000)
 
 # IP TOS
 NXM_OF_ETH_TYPE(0800), NXM_OF_IP_TOS(f0)
-nx_pull_match() returned error 44010102 (type OFPET_BAD_REQUEST, code NXBRC_NXM_BAD_VALUE)
-nx_pull_match() returned error 44010104 (type OFPET_BAD_REQUEST, code NXBRC_NXM_BAD_PREREQ)
+nx_pull_match() returned error NXBRC_NXM_BAD_VALUE
+nx_pull_match() returned error NXBRC_NXM_BAD_PREREQ
 
 # IP ECN
 NXM_OF_ETH_TYPE(0800), NXM_NX_IP_ECN(03)
-nx_pull_match() returned error 44010102 (type OFPET_BAD_REQUEST, code NXBRC_NXM_BAD_VALUE)
-nx_pull_match() returned error 44010104 (type OFPET_BAD_REQUEST, code NXBRC_NXM_BAD_PREREQ)
+nx_pull_match() returned error NXBRC_NXM_BAD_VALUE
+nx_pull_match() returned error NXBRC_NXM_BAD_PREREQ
 
 # IP protocol
 NXM_OF_ETH_TYPE(0800), NXM_OF_IP_PROTO(01)
 NXM_OF_ETH_TYPE(0800), NXM_OF_IP_PROTO(05)
-nx_pull_match() returned error 44010104 (type OFPET_BAD_REQUEST, code NXBRC_NXM_BAD_PREREQ)
+nx_pull_match() returned error NXBRC_NXM_BAD_PREREQ
 
 # IP TTL
 NXM_OF_ETH_TYPE(0800), NXM_NX_IP_TTL(80)
 NXM_OF_ETH_TYPE(86dd), NXM_NX_IP_TTL(ff)
-nx_pull_match() returned error 44010104 (type OFPET_BAD_REQUEST, code NXBRC_NXM_BAD_PREREQ)
+nx_pull_match() returned error NXBRC_NXM_BAD_PREREQ
 
 # IP source
 NXM_OF_ETH_TYPE(0800), NXM_OF_IP_SRC(ac100014)
 NXM_OF_ETH_TYPE(0800), NXM_OF_IP_SRC_W(c0a80000/ffff0000)
-nx_pull_match() returned error 44010104 (type OFPET_BAD_REQUEST, code NXBRC_NXM_BAD_PREREQ)
-nx_pull_match() returned error 44010104 (type OFPET_BAD_REQUEST, code NXBRC_NXM_BAD_PREREQ)
+nx_pull_match() returned error NXBRC_NXM_BAD_PREREQ
+nx_pull_match() returned error NXBRC_NXM_BAD_PREREQ
 
 # IP destination
 NXM_OF_ETH_TYPE(0800), NXM_OF_IP_DST(ac100014)
 NXM_OF_ETH_TYPE(0800), NXM_OF_IP_DST_W(c0a80000/ffff0000)
-nx_pull_match() returned error 44010104 (type OFPET_BAD_REQUEST, code NXBRC_NXM_BAD_PREREQ)
-nx_pull_match() returned error 44010104 (type OFPET_BAD_REQUEST, code NXBRC_NXM_BAD_PREREQ)
+nx_pull_match() returned error NXBRC_NXM_BAD_PREREQ
+nx_pull_match() returned error NXBRC_NXM_BAD_PREREQ
 
 # TCP source port
 NXM_OF_ETH_TYPE(0800), NXM_OF_IP_PROTO(06), NXM_OF_TCP_SRC(4231)
-nx_pull_match() returned error 44010104 (type OFPET_BAD_REQUEST, code NXBRC_NXM_BAD_PREREQ)
+nx_pull_match() returned error NXBRC_NXM_BAD_PREREQ
 
 # TCP destination port
 NXM_OF_ETH_TYPE(0800), NXM_OF_IP_PROTO(06), NXM_OF_TCP_DST(4231)
-nx_pull_match() returned error 44010104 (type OFPET_BAD_REQUEST, code NXBRC_NXM_BAD_PREREQ)
+nx_pull_match() returned error NXBRC_NXM_BAD_PREREQ
 
 # UDP source port
 NXM_OF_ETH_TYPE(0800), NXM_OF_IP_PROTO(11), NXM_OF_UDP_SRC(8732)
-nx_pull_match() returned error 44010104 (type OFPET_BAD_REQUEST, code NXBRC_NXM_BAD_PREREQ)
+nx_pull_match() returned error NXBRC_NXM_BAD_PREREQ
 
 # UDP destination port
 NXM_OF_ETH_TYPE(0800), NXM_OF_IP_PROTO(11), NXM_OF_UDP_DST(1782)
-nx_pull_match() returned error 44010104 (type OFPET_BAD_REQUEST, code NXBRC_NXM_BAD_PREREQ)
+nx_pull_match() returned error NXBRC_NXM_BAD_PREREQ
 
 # ICMP type
 NXM_OF_ETH_TYPE(0800), NXM_OF_IP_PROTO(01), NXM_OF_ICMP_TYPE(12)
-nx_pull_match() returned error 44010104 (type OFPET_BAD_REQUEST, code NXBRC_NXM_BAD_PREREQ)
+nx_pull_match() returned error NXBRC_NXM_BAD_PREREQ
 
 # ICMP code
 NXM_OF_ETH_TYPE(0800), NXM_OF_IP_PROTO(01), NXM_OF_ICMP_CODE(12)
-nx_pull_match() returned error 44010104 (type OFPET_BAD_REQUEST, code NXBRC_NXM_BAD_PREREQ)
-nx_pull_match() returned error 44010104 (type OFPET_BAD_REQUEST, code NXBRC_NXM_BAD_PREREQ)
-nx_pull_match() returned error 44010104 (type OFPET_BAD_REQUEST, code NXBRC_NXM_BAD_PREREQ)
+nx_pull_match() returned error NXBRC_NXM_BAD_PREREQ
+nx_pull_match() returned error NXBRC_NXM_BAD_PREREQ
+nx_pull_match() returned error NXBRC_NXM_BAD_PREREQ
 
 # ARP opcode
 NXM_OF_ETH_TYPE(0806), NXM_OF_ARP_OP(0001)
-nx_pull_match() returned error 44010102 (type OFPET_BAD_REQUEST, code NXBRC_NXM_BAD_VALUE)
-nx_pull_match() returned error 44010104 (type OFPET_BAD_REQUEST, code NXBRC_NXM_BAD_PREREQ)
-nx_pull_match() returned error 44010104 (type OFPET_BAD_REQUEST, code NXBRC_NXM_BAD_PREREQ)
-nx_pull_match() returned error 44010105 (type OFPET_BAD_REQUEST, code NXBRC_NXM_DUP_TYPE)
+nx_pull_match() returned error NXBRC_NXM_BAD_VALUE
+nx_pull_match() returned error NXBRC_NXM_BAD_PREREQ
+nx_pull_match() returned error NXBRC_NXM_BAD_PREREQ
+nx_pull_match() returned error NXBRC_NXM_DUP_TYPE
 
 # ARP source protocol address
 NXM_OF_ETH_TYPE(0806), NXM_OF_ARP_SPA(ac100014)
 NXM_OF_ETH_TYPE(0806), NXM_OF_ARP_SPA_W(c0a81200/ffffff00)
-nx_pull_match() returned error 44010104 (type OFPET_BAD_REQUEST, code NXBRC_NXM_BAD_PREREQ)
-nx_pull_match() returned error 44010104 (type OFPET_BAD_REQUEST, code NXBRC_NXM_BAD_PREREQ)
+nx_pull_match() returned error NXBRC_NXM_BAD_PREREQ
+nx_pull_match() returned error NXBRC_NXM_BAD_PREREQ
 
 # ARP destination protocol address
 NXM_OF_ETH_TYPE(0806), NXM_OF_ARP_TPA(ac100014)
 NXM_OF_ETH_TYPE(0806), NXM_OF_ARP_TPA_W(c0a81200/ffffff00)
-nx_pull_match() returned error 44010104 (type OFPET_BAD_REQUEST, code NXBRC_NXM_BAD_PREREQ)
-nx_pull_match() returned error 44010104 (type OFPET_BAD_REQUEST, code NXBRC_NXM_BAD_PREREQ)
+nx_pull_match() returned error NXBRC_NXM_BAD_PREREQ
+nx_pull_match() returned error NXBRC_NXM_BAD_PREREQ
 
 # ARP source hardware address
 NXM_OF_ETH_TYPE(0806), NXM_NX_ARP_SHA(0002e30f80a4)
-nx_pull_match() returned error 44010104 (type OFPET_BAD_REQUEST, code NXBRC_NXM_BAD_PREREQ)
-nx_pull_match() returned error 44010104 (type OFPET_BAD_REQUEST, code NXBRC_NXM_BAD_PREREQ)
+nx_pull_match() returned error NXBRC_NXM_BAD_PREREQ
+nx_pull_match() returned error NXBRC_NXM_BAD_PREREQ
 
 # ARP destination hardware address
 NXM_OF_ETH_TYPE(0806), NXM_NX_ARP_THA(0002e30f80a4)
-nx_pull_match() returned error 44010104 (type OFPET_BAD_REQUEST, code NXBRC_NXM_BAD_PREREQ)
-nx_pull_match() returned error 44010104 (type OFPET_BAD_REQUEST, code NXBRC_NXM_BAD_PREREQ)
+nx_pull_match() returned error NXBRC_NXM_BAD_PREREQ
+nx_pull_match() returned error NXBRC_NXM_BAD_PREREQ
 
 # IPv6 source
 NXM_OF_ETH_TYPE(86dd), NXM_NX_IPV6_SRC(20010db83c4d00010002000300040005)
-nx_pull_match() returned error 44010104 (type OFPET_BAD_REQUEST, code NXBRC_NXM_BAD_PREREQ)
+nx_pull_match() returned error NXBRC_NXM_BAD_PREREQ
 NXM_OF_ETH_TYPE(86dd), NXM_NX_IPV6_SRC_W(20010db83c4d00010000000000000000/ffffffffffffffff0000000000000000)
-nx_pull_match() returned error 44010104 (type OFPET_BAD_REQUEST, code NXBRC_NXM_BAD_PREREQ)
+nx_pull_match() returned error NXBRC_NXM_BAD_PREREQ
 
 # IPv6 destination
 NXM_OF_ETH_TYPE(86dd), NXM_NX_IPV6_DST(20010db83c4d00010002000300040005)
-nx_pull_match() returned error 44010104 (type OFPET_BAD_REQUEST, code NXBRC_NXM_BAD_PREREQ)
+nx_pull_match() returned error NXBRC_NXM_BAD_PREREQ
 NXM_OF_ETH_TYPE(86dd), NXM_NX_IPV6_DST_W(20010db83c4d00010000000000000000/ffffffffffffffff0000000000000000)
-nx_pull_match() returned error 44010104 (type OFPET_BAD_REQUEST, code NXBRC_NXM_BAD_PREREQ)
+nx_pull_match() returned error NXBRC_NXM_BAD_PREREQ
 
 # ND source hardware address
 NXM_OF_ETH_TYPE(86dd), NXM_OF_IP_PROTO(3a), NXM_NX_ICMPV6_TYPE(87), NXM_NX_ND_TARGET(20010db83c4d00010002000300040005), NXM_NX_ND_SLL(0002e30f80a4)
-nx_pull_match() returned error 44010104 (type OFPET_BAD_REQUEST, code NXBRC_NXM_BAD_PREREQ)
-nx_pull_match() returned error 44010104 (type OFPET_BAD_REQUEST, code NXBRC_NXM_BAD_PREREQ)
-nx_pull_match() returned error 44010104 (type OFPET_BAD_REQUEST, code NXBRC_NXM_BAD_PREREQ)
+nx_pull_match() returned error NXBRC_NXM_BAD_PREREQ
+nx_pull_match() returned error NXBRC_NXM_BAD_PREREQ
+nx_pull_match() returned error NXBRC_NXM_BAD_PREREQ
 
 # ND destination hardware address
 NXM_OF_ETH_TYPE(86dd), NXM_OF_IP_PROTO(3a), NXM_NX_ICMPV6_TYPE(88), NXM_NX_ND_TARGET(20010db83c4d00010002000300040005), NXM_NX_ND_TLL(0002e30f80a4)
-nx_pull_match() returned error 44010104 (type OFPET_BAD_REQUEST, code NXBRC_NXM_BAD_PREREQ)
-nx_pull_match() returned error 44010104 (type OFPET_BAD_REQUEST, code NXBRC_NXM_BAD_PREREQ)
-nx_pull_match() returned error 44010104 (type OFPET_BAD_REQUEST, code NXBRC_NXM_BAD_PREREQ)
+nx_pull_match() returned error NXBRC_NXM_BAD_PREREQ
+nx_pull_match() returned error NXBRC_NXM_BAD_PREREQ
+nx_pull_match() returned error NXBRC_NXM_BAD_PREREQ
 
 # IPv4 fragments.
 NXM_OF_ETH_TYPE(0800), NXM_NX_IP_FRAG(00)
@@ -520,7 +520,7 @@ NXM_OF_ETH_TYPE(0800), NXM_NX_IP_FRAG_W(00/02)
 NXM_OF_ETH_TYPE(0800), NXM_NX_IP_FRAG_W(01/01)
 NXM_OF_ETH_TYPE(0800), NXM_NX_IP_FRAG_W(02/02)
 NXM_OF_ETH_TYPE(0800), NXM_NX_IP_FRAG(03)
-nx_pull_match() returned error 44010102 (type OFPET_BAD_REQUEST, code NXBRC_NXM_BAD_VALUE)
+nx_pull_match() returned error NXBRC_NXM_BAD_VALUE
 
 # IPv6 fragments.
 NXM_OF_ETH_TYPE(86dd), NXM_NX_IP_FRAG(00)
@@ -533,7 +533,7 @@ NXM_OF_ETH_TYPE(86dd), NXM_NX_IP_FRAG_W(00/02)
 NXM_OF_ETH_TYPE(86dd), NXM_NX_IP_FRAG_W(01/01)
 NXM_OF_ETH_TYPE(86dd), NXM_NX_IP_FRAG_W(02/02)
 NXM_OF_ETH_TYPE(86dd), NXM_NX_IP_FRAG(03)
-nx_pull_match() returned error 44010102 (type OFPET_BAD_REQUEST, code NXBRC_NXM_BAD_VALUE)
+nx_pull_match() returned error NXBRC_NXM_BAD_VALUE
 
 # Tunnel ID.
 NXM_NX_TUN_ID(00000000abcdef01)
@@ -544,7 +544,7 @@ NXM_NX_REG0(acebdf56)
 NXM_NX_REG0_W(a0e0d050/f0f0f0f0)
 
 # Invalid field number.
-nx_pull_match() returned error 44010101 (type OFPET_BAD_REQUEST, code NXBRC_NXM_BAD_TYPE)
+nx_pull_match() returned error NXBRC_NXM_BAD_TYPE
 
 # Unimplemented registers.
 #
@@ -552,8 +552,8 @@ nx_pull_match() returned error 44010101 (type OFPET_BAD_REQUEST, code NXBRC_NXM_
 # registers are implemented.
 NXM_NX_REG0(12345678)
 NXM_NX_REG0_W(12345678/12345678)
-nx_pull_match() returned error 44010101 (type OFPET_BAD_REQUEST, code NXBRC_NXM_BAD_TYPE)
-nx_pull_match() returned error 44010101 (type OFPET_BAD_REQUEST, code NXBRC_NXM_BAD_TYPE)
+nx_pull_match() returned error NXBRC_NXM_BAD_TYPE
+nx_pull_match() returned error NXBRC_NXM_BAD_TYPE
 ])
 AT_CLEANUP
 
diff --git a/utilities/ovs-ofctl.c b/utilities/ovs-ofctl.c
index cf77300..bd184b8 100644
--- a/utilities/ovs-ofctl.c
+++ b/utilities/ovs-ofctl.c
@@ -36,6 +36,7 @@
 #include "netlink.h"
 #include "nx-match.h"
 #include "odp-util.h"
+#include "ofp-errors.h"
 #include "ofp-parse.h"
 #include "ofp-print.h"
 #include "ofp-util.h"
@@ -1455,8 +1456,8 @@ do_parse_nx_match(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
     while (!ds_get_line(&in, stdin)) {
         struct ofpbuf nx_match;
         struct cls_rule rule;
+        enum ofperr error;
         int match_len;
-        int error;
         char *s;
 
         /* Delete comments, skip blank lines. */
@@ -1492,8 +1493,8 @@ do_parse_nx_match(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
             puts(out);
             free(out);
         } else {
-            printf("nx_pull_match() returned error %x (%s)\n", error,
-                   ofputil_error_to_string(error));
+            printf("nx_pull_match() returned error %s\n",
+                   ofperr_get_name(error));
         }
 
         ofpbuf_uninit(&nx_match);
-- 
1.7.2.5




More information about the dev mailing list