[ovs-dev] [of1.2 errors 4/4] Add error codes for Open Flow v1.2

Ben Pfaff blp at nicira.com
Mon Mar 26 20:59:20 UTC 2012


From: Simon Horman <horms at verge.net.au>

* Where Open Flow 1.2 breaks apart error codes defined
  in previous versions, provide all new definitions to
  previous versions and map the numeric error code to
  the first first definition supplied in ofp-errors.h.
  The case handled so far is:
  OFPERR_OFPBIC_BAD_EXP_TYPE -> { OFPERR_OFPBIC_BAD_EXPERIMENTER,
                                  OFPERR_OFPBIC_BAD_EXP_TYPE }

* Where Open Flow 1.2 adds error codes that were previously
  defined as Nicira extension errors define the later in terms
  of the new codes.

Signed-off-by: Simon Horman <horms at verge.net.au>
[blp at nicira.com added better error checking in extract-ofp-errors, added
 unit tests, miscellaneous cleanup]
Signed-off-by: Ben Pfaff <blp at nicira.com>
---
 build-aux/extract-ofp-errors |   92 ++++++++++++++++++++++++------
 lib/nx-match.c               |   16 +++---
 lib/ofp-errors.c             |   61 +++++++++++++++++++-
 lib/ofp-errors.h             |  128 ++++++++++++++++++++++++++++++++++++------
 ofproto/ofproto.c            |    2 +-
 tests/automake.mk            |    1 +
 tests/ofp-errors.at          |   97 +++++++++++++++++++++++++++++++
 tests/ofp-print.at           |   60 +-------------------
 tests/ovs-ofctl.at           |   94 +++++++++++++++---------------
 tests/testsuite.at           |    1 +
 utilities/ovs-ofctl.c        |   29 ++++++++++
 11 files changed, 427 insertions(+), 154 deletions(-)
 create mode 100644 tests/ofp-errors.at

diff --git a/build-aux/extract-ofp-errors b/build-aux/extract-ofp-errors
index 4b3d46b..7054408 100755
--- a/build-aux/extract-ofp-errors
+++ b/build-aux/extract-ofp-errors
@@ -66,8 +66,14 @@ def getToken():
                 token = None
                 return False
 
-def fatal(msg):
+n_errors = 0
+def error(msg):
+    global n_errors
     sys.stderr.write("%s:%d: %s\n" % (fileName, lineNumber, msg))
+    n_errors += 1
+
+def fatal(msg):
+    error(msg)
     sys.exit(1)
 
 def skipDirective():
@@ -145,10 +151,13 @@ def extract_ofp_errors(filenames):
     names = []
     domain = {}
     reverse = {}
-    for domain_name in ("OF1.0", "OF1.1", "NX1.0", "NX1.1"):
+    for domain_name in ("OF1.0", "OF1.1", "OF1.2", "NX1.0", "NX1.1"):
         domain[domain_name] = {}
         reverse[domain_name] = {}
 
+    n_errors = 0
+    expected_errors = {}
+
     global fileName
     for fileName in filenames:
         global inputFile
@@ -168,13 +177,10 @@ def extract_ofp_errors(filenames):
             elif re.match('}', line):
                 break
 
-            m = re.match('\s+/\* ((?:.(?!\.  ))+.)\.  (.*)$', line)
-            if not m:
+            if not line.lstrip().startswith('/*'):
                 fatal("unexpected syntax between errors")
 
-            dsts, comment = m.groups()
-
-            comment.rstrip()
+            comment = line.lstrip()[2:].strip()
             while not comment.endswith('*/'):
                 getLine()
                 if line.startswith('/*') or not line or line.isspace():
@@ -182,6 +188,17 @@ def extract_ofp_errors(filenames):
                 comment += ' %s' % line.lstrip('* \t').rstrip(' \t\r\n')
             comment = comment[:-2].rstrip()
 
+            m = re.match('Expected: (.*)\.$', comment)
+            if m:
+                expected_errors[m.group(1)] = (fileName, lineNumber)
+                continue
+
+            m = re.match('((?:.(?!\.  ))+.)\.  (.*)$', comment)
+            if not m:
+                fatal("unexpected syntax between errors")
+
+            dsts, comment = m.groups()
+
             getLine()
             m = re.match('\s+(?:OFPERR_((?:OFP|NX)[A-Z0-9_]+))(\s*=\s*OFPERR_OFS)?,',
                          line)
@@ -194,35 +211,68 @@ def extract_ofp_errors(filenames):
             names.append(enum)
 
             for dst in dsts.split(', '):
-                m = re.match(r'([A-Z0-9.+]+)\((\d+)(?:,(\d+))?\)$', dst)
+                m = re.match(r'([A-Z0-9.+]+)\((\d+|(0x)[0-9a-fA-F]+)(?:,(\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))
+                    base = 16
+                else:
+                    base = 10
+                type_ = int(m.group(2), base)
+                if m.group(4):
+                    code = int(m.group(4))
                 else:
                     code = None
 
-                target_map = {"OF1.0+": ("OF1.0", "OF1.1"),
-                              "OF1.1+": ("OF1.1",),
+                target_map = {"OF1.0+": ("OF1.0", "OF1.1", "OF1.2"),
+                              "OF1.1+": ("OF1.1", "OF1.2"),
+                              "OF1.2+": ("OF1.2",),
                               "OF1.0":  ("OF1.0",),
                               "OF1.1":  ("OF1.1",),
-                              "NX1.0+": ("OF1.0", "OF1.1"),
+                              "OF1.2":  ("OF1.2",),
+                              "NX1.0+": ("OF1.0", "OF1.1", "OF1.2"),
                               "NX1.0":  ("OF1.0",),
-                              "NX1.1":  ("OF1.1",)}
+                              "NX1.1":  ("OF1.1",),
+                              "NX1.2":  ("OF1.2",)}
                 if targets not in target_map:
                     fatal("%s: unknown error domain" % targets)
                 for target in target_map[targets]:
-                    if type_ not in domain[target]:
-                        domain[target][type_] = {}
+                    domain[target].setdefault(type_, {})
                     if code in domain[target][type_]:
-                        fatal("%s: duplicate assignment in domain" % dst)
-                    domain[target][type_][code] = enum
+                        msg = "%d,%d in %s means both %s and %s" % (
+                            type_, code, target,
+                            domain[target][type_][code][0], enum)
+                        if msg in expected_errors:
+                            del expected_errors[msg]
+                        else:
+                            error("%s: %s." % (dst, msg))
+                            sys.stderr.write("%s:%d: %s: Here is the location "
+                                             "of the previous definition.\n"
+                                             % (domain[target][type_][code][1],
+                                                domain[target][type_][code][2],
+                                                dst))
+                    else:
+                        domain[target][type_][code] = (enum, fileName,
+                                                       lineNumber)
+
+                    if enum in reverse[target]:
+                        error("%s: %s in %s means both %d,%d and %d,%d." %
+                              (dst, enum, target,
+                               reverse[target][enum][0],
+                               reverse[target][enum][1],
+                               type_, code))
                     reverse[target][enum] = (type_, code)
 
         inputFile.close()
 
+    for fn, ln in expected_errors.itervalues():
+        sys.stderr.write("%s:%d: expected duplicate not used.\n" % (fn, ln))
+        n_errors += 1
+
+    if n_errors:
+        sys.exit(1)
+
     print """\
 /* Generated automatically; do not modify!     -*- buffer-read-only: t -*- */
 
@@ -254,12 +304,17 @@ static enum ofperr
 %s_decode(uint16_t type, uint16_t code)
 {
     switch ((type << 16) | code) {""" % name
+        found = []
         for enum in names:
             if enum not in map:
                 continue
             type_, code = map[enum]
             if code is None:
                 continue
+            value = (type_ << 16) | code
+            if value in found:
+                continue
+            found.append(value)
             print "    case (%d << 16) | %d:" % (type_, code)
             print "        return OFPERR_%s;" % enum
         print """\
@@ -307,6 +362,7 @@ const struct ofperr_domain %s = {
 
     output_domain(reverse["OF1.0"], "ofperr_of10", "OpenFlow 1.0", 0x01)
     output_domain(reverse["OF1.1"], "ofperr_of11", "OpenFlow 1.1", 0x02)
+    output_domain(reverse["OF1.2"], "ofperr_of12", "OpenFlow 1.2", 0x03)
 
 if __name__ == '__main__':
     if '--help' in sys.argv:
diff --git a/lib/nx-match.c b/lib/nx-match.c
index 3ead9b6..0e61d35 100644
--- a/lib/nx-match.c
+++ b/lib/nx-match.c
@@ -103,7 +103,7 @@ nx_pull_match__(struct ofpbuf *b, unsigned int match_len, bool strict,
         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 OFPERR_OFPBRC_BAD_LEN;
+        return OFPERR_OFPBMC_BAD_LEN;
     }
 
     cls_rule_init_catchall(rule, priority);
@@ -119,21 +119,21 @@ nx_pull_match__(struct ofpbuf *b, unsigned int match_len, bool strict,
         mf = mf_from_nxm_header(header);
         if (!mf) {
             if (strict) {
-                error = OFPERR_NXBRC_NXM_BAD_TYPE;
+                error = OFPERR_OFPBMC_BAD_FIELD;
             } else {
                 continue;
             }
         } else if (!mf_are_prereqs_ok(mf, &rule->flow)) {
-            error = OFPERR_NXBRC_NXM_BAD_PREREQ;
+            error = OFPERR_OFPBMC_BAD_PREREQ;
         } else if (!mf_is_all_wild(mf, &rule->wc)) {
-            error = OFPERR_NXBRC_NXM_DUP_TYPE;
+            error = OFPERR_OFPBMC_DUP_FIELD;
         } else {
             unsigned int width = mf->n_bytes;
             union mf_value value;
 
             memcpy(&value, p + 4, width);
             if (!mf_is_value_valid(mf, &value)) {
-                error = OFPERR_NXBRC_NXM_BAD_VALUE;
+                error = OFPERR_OFPBMC_BAD_VALUE;
             } else if (!NXM_HASMASK(header)) {
                 error = 0;
                 mf_set_value(mf, &value, rule);
@@ -142,7 +142,7 @@ nx_pull_match__(struct ofpbuf *b, unsigned int match_len, bool strict,
 
                 memcpy(&mask, p + 4 + width, width);
                 if (!mf_is_mask_valid(mf, &mask)) {
-                    error = OFPERR_NXBRC_NXM_BAD_MASK;
+                    error = OFPERR_OFPBMC_BAD_MASK;
                 } else {
                     error = 0;
                     mf_set(mf, &value, &mask, rule);
@@ -153,7 +153,7 @@ nx_pull_match__(struct ofpbuf *b, unsigned int match_len, bool strict,
         /* Check if the match is for a cookie rather than a classifier rule. */
         if ((header == NXM_NX_COOKIE || header == NXM_NX_COOKIE_W) && cookie) {
             if (*cookie_mask) {
-                error = OFPERR_NXBRC_NXM_DUP_TYPE;
+                error = OFPERR_OFPBMC_DUP_FIELD;
             } else {
                 unsigned int width = sizeof *cookie;
 
@@ -178,7 +178,7 @@ nx_pull_match__(struct ofpbuf *b, unsigned int match_len, bool strict,
         }
     }
 
-    return match_len ? OFPERR_NXBRC_NXM_INVALID : 0;
+    return match_len ? OFPERR_OFPBMC_BAD_LEN : 0;
 }
 
 /* Parses the nx_match formatted match description in 'b' with length
diff --git a/lib/ofp-errors.c b/lib/ofp-errors.c
index 028475e..e1349b0 100644
--- a/lib/ofp-errors.c
+++ b/lib/ofp-errors.c
@@ -25,9 +25,17 @@ ofperr_domain_from_version(uint8_t version)
 {
     return (version == ofperr_of10.version ? &ofperr_of10
             : version == ofperr_of11.version ? &ofperr_of11
+            : version == ofperr_of12.version ? &ofperr_of12
             : NULL);
 }
 
+/* Returns the name (e.g. "OpenFlow 1.0") of OpenFlow error domain 'domain'. */
+const char *
+ofperr_domain_get_name(const struct ofperr_domain *domain)
+{
+    return domain->name;
+}
+
 /* Returns true if 'error' is a valid OFPERR_* value, false otherwise. */
 bool
 ofperr_is_valid(enum ofperr error)
@@ -97,6 +105,24 @@ ofperr_get_name(enum ofperr error)
             : "<invalid>");
 }
 
+/* Returns the OFPERR_* value that corresponds for 'name', 0 if none exists.
+ * For example, returns OFPERR_OFPHFC_INCOMPATIBLE if 'name' is
+ * "OFPHFC_INCOMPATIBLE".
+ *
+ * This is probably useful only for debugging and testing. */
+enum ofperr
+ofperr_from_name(const char *name)
+{
+    int i;
+
+    for (i = 0; i < OFPERR_N_ERRORS; i++) {
+        if (!strcmp(name, error_names[i])) {
+            return i + OFPERR_OFS;
+        }
+    }
+    return 0;
+}
+
 /* 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. */
@@ -108,6 +134,15 @@ ofperr_get_description(enum ofperr error)
             : "<invalid>");
 }
 
+static const struct pair *
+ofperr_get_pair__(enum ofperr error, const struct ofperr_domain *domain)
+{
+    size_t ofs = error - OFPERR_OFS;
+
+    assert(ofperr_is_valid(error));
+    return &domain->errors[ofs];
+}
+
 static struct ofpbuf *
 ofperr_encode_msg__(enum ofperr error, const struct ofperr_domain *domain,
                     ovs_be32 xid, const void *data, size_t data_len)
@@ -115,7 +150,6 @@ ofperr_encode_msg__(enum ofperr error, const struct ofperr_domain *domain,
     struct ofp_error_msg *oem;
     const struct pair *pair;
     struct ofpbuf *buf;
-    size_t ofs;
 
     if (!domain) {
         return NULL;
@@ -140,8 +174,7 @@ ofperr_encode_msg__(enum ofperr error, const struct ofperr_domain *domain,
         return NULL;
     }
 
-    ofs = error - OFPERR_OFS;
-    pair = &domain->errors[ofs];
+    pair = ofperr_get_pair__(error, domain);
     if (!ofperr_is_nx_extension(error)) {
         oem = make_openflow_xid(data_len + sizeof *oem, OFPT_ERROR, xid, &buf);
         oem->type = htons(pair->type);
@@ -210,6 +243,28 @@ ofperr_encode_hello(enum ofperr error, const struct ofperr_domain *domain,
     return ofperr_encode_msg__(error, domain, htonl(0), s, strlen(s));
 }
 
+/* Returns the value that would go into an OFPT_ERROR message's 'type' for
+ * encoding 'error' in 'domain'.  Returns -1 if 'error' is not encodable in
+ * 'domain'.
+ *
+ * 'error' must be a valid OFPERR_* code, as checked by ofperr_is_valid(). */
+int
+ofperr_get_type(enum ofperr error, const struct ofperr_domain *domain)
+{
+    return ofperr_get_pair__(error, domain)->type;
+}
+
+/* Returns the value that would go into an OFPT_ERROR message's 'code' for
+ * encoding 'error' in 'domain'.  Returns -1 if 'error' is not encodable in
+ * 'domain' or if 'error' represents a category rather than a specific error.
+ *
+ * 'error' must be a valid OFPERR_* code, as checked by ofperr_is_valid(). */
+int
+ofperr_get_code(enum ofperr error, const struct ofperr_domain *domain)
+{
+    return ofperr_get_pair__(error, domain)->code;
+}
+
 /* Tries to decodes 'oh', which should be an OpenFlow OFPT_ERROR message.
  * Returns an OFPERR_* constant on success, 0 on failure.
  *
diff --git a/lib/ofp-errors.h b/lib/ofp-errors.h
index bd6095b..40e5a61 100644
--- a/lib/ofp-errors.h
+++ b/lib/ofp-errors.h
@@ -44,6 +44,11 @@ struct ofp_header;
 #define OFPERR_OFS (1 << 30)
 
 enum ofperr {
+/* Expected duplications. */
+
+    /* Expected: 3,5 in OF1.1 means both OFPBIC_BAD_EXPERIMENTER and
+     * OFPBIC_BAD_EXP_TYPE. */
+
 /* ## ------------------ ## */
 /* ## OFPET_HELLO_FAILED ## */
 /* ## ------------------ ## */
@@ -95,6 +100,15 @@ enum ofperr {
     /* OF1.1+(1,9).  Specified table-id invalid or does not exist. */
     OFPERR_OFPBRC_BAD_TABLE_ID,
 
+    /* OF1.2+(1,10).  Denied because controller is slave. */
+    OFPERR_OFPBRC_IS_SLAVE,
+
+    /* OF1.2+(1,11).  Invalid port. */
+    OFPERR_OFPBRC_BAD_PORT,
+
+    /* OF1.2+(1,12).  Invalid packet in packet-out. */
+    OFPERR_OFPBRC_BAD_PACKET,
+
     /* NX1.0+(1,256).  Invalid NXM flow match. */
     OFPERR_NXBRC_NXM_INVALID,
 
@@ -102,24 +116,9 @@ enum ofperr {
      * nxm_hasmask or nxm_length or both, is invalid or not implemented. */
     OFPERR_NXBRC_NXM_BAD_TYPE,
 
-    /* NX1.0+(1,258).  Invalid nxm_value. */
-    OFPERR_NXBRC_NXM_BAD_VALUE,
-
-    /* NX1.0+(1,259).  Invalid nxm_mask. */
-    OFPERR_NXBRC_NXM_BAD_MASK,
-
-    /* NX1.0+(1,260).  A prerequisite was not met. */
-    OFPERR_NXBRC_NXM_BAD_PREREQ,
-
-    /* NX1.0+(1,261).  A given nxm_type was specified more than once. */
-    OFPERR_NXBRC_NXM_DUP_TYPE,
-
     /* NX1.0+(1,512).  A request specified a nonexistent table ID. */
     OFPERR_NXBRC_BAD_TABLE_ID,
 
-    /* NX1.0+(1,513).  NXT_ROLE_REQUEST specified an invalid role. */
-    OFPERR_NXBRC_BAD_ROLE,
-
     /* NX1.0+(1,514).  The in_port in an ofp_packet_out request is invalid. */
     OFPERR_NXBRC_BAD_IN_PORT,
 
@@ -177,6 +176,15 @@ enum ofperr {
     /* OF1.1+(2,12).  Actions uses an unsupported tag/encap. */
     OFPERR_OFPBAC_BAD_TAG,
 
+    /* OF1.2+(2,13).  Unsupported type in SET_FIELD action. */
+    OFPERR_OFPBAC_SET_TYPE,
+
+    /* OF1.2+(2,14).  Length problem in SET_FIELD action. */
+    OFPERR_OFPBAC_SET_LEN,
+
+    /* OF1.2+(2,15).  Bad argument in SET_FIELD action. */
+    OFPERR_OFPBAC_ARGUMENT,
+
     /* NX1.0+(2,256).  Must-be-zero action argument had nonzero value. */
     OFPERR_NXBAC_MUST_BE_ZERO,
 
@@ -202,8 +210,17 @@ enum ofperr {
     /* 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,
+    /* OF1.1+(3,5).  Unknown experimenter id specified. */
+    OFPERR_OFPBIC_BAD_EXPERIMENTER,
+
+    /* OF1.1(3,5), OF1.2+(3,6).  Unknown instruction for experimenter id. */
+    OFPERR_OFPBIC_BAD_EXP_TYPE,
+
+    /* OF1.2+(3,7).  Length problem in instructions. */
+    OFPERR_OFPBIC_BAD_LEN,
+
+    /* OF1.2+(3,8).  Permissions error. */
+    OFPERR_OFPBIC_EPERM,
 
 /* ## --------------- ## */
 /* ## OFPET_BAD_MATCH ## */
@@ -235,9 +252,24 @@ enum ofperr {
     /* OF1.1+(4,6).  Unsupported field in the match. */
     OFPERR_OFPBMC_BAD_FIELD,
 
-    /* OF1.1+(4,7).  Unsupported value in a match field. */
+    /* NX1.0(1,258), NX1.1(1,258), OF1.2+(4,7).  Unsupported value in a match
+     * field. */
     OFPERR_OFPBMC_BAD_VALUE,
 
+    /* NX1.0(1,259), NX1.1(1,259), OF1.2+(4,8).  Unsupported mask specified in
+     * the match, field is not dl-address or nw-address. */
+    OFPERR_OFPBMC_BAD_MASK,
+
+    /* NX1.0(1,260), NX1.1(1,260), OF1.2+(4,9).  A prerequisite was not met. */
+    OFPERR_OFPBMC_BAD_PREREQ,
+
+    /* NX1.0(1,261), NX1.1(1,261), OF1.2+(4,10).  A field type was
+     * duplicated. */
+    OFPERR_OFPBMC_DUP_FIELD,
+
+    /* OF1.2+(4,11).  Permissions error. */
+    OFPERR_OFPBMC_EPERM,
+
 /* ## --------------------- ## */
 /* ## OFPET_FLOW_MOD_FAILED ## */
 /* ## --------------------- ## */
@@ -274,6 +306,9 @@ enum ofperr {
     /* OF1.0(3,4), OF1.1+(5,6).  Unsupported or unknown command. */
     OFPERR_OFPFMFC_BAD_COMMAND,
 
+    /* OF1.2+(5,7).  Unsupported or unknown flags. */
+    OFPERR_OFPFMFC_BAD_FLAGS,
+
     /* OF1.0(3,5).  Unsupported action list - cannot process in the order
      * specified. */
     OFPERR_OFPFMFC_UNSUPPORTED,
@@ -325,6 +360,25 @@ enum ofperr {
      * modify a non-existent group. */
     OFPERR_OFPGMFC_UNKNOWN_GROUP,
 
+    /* OF1.2+(6,9).  Group not deleted because another
+                    group is forwarding to it. */
+    OFPERR_OFPGMFC_CHAINED_GROUP,
+
+    /* OF1.2+(6,10).  Unsupported or unknown group type. */
+    OFPERR_OFPGMFC_BAD_TYPE,
+
+    /* OF1.2+(6,11).  Unsupported or unknown command. */
+    OFPERR_OFPGMFC_BAD_COMMAND,
+
+    /* OF1.2+(6,12).  Error in bucket. */
+    OFPERR_OFPGMFC_OFPGMFC_BAD_BUCKET,
+
+    /* OF1.2+(6,13).  Error in watch port/group. */
+    OFPERR_OFPGMFC_OFPGMFC_BAD_WATCH,
+
+    /* OF1.2+(6,14).  Permissions error. */
+    OFPERR_OFPGMFC_OFPGMFC_EPERM,
+
 /* ## --------------------- ## */
 /* ## OFPET_PORT_MOD_FAILED ## */
 /* ## --------------------- ## */
@@ -345,6 +399,9 @@ enum ofperr {
     /* OF1.1+(7,3).  Specified advertise is invalid. */
     OFPERR_OFPPMFC_BAD_ADVERTISE,
 
+    /* OF1.2+(7,4).  Permissions error. */
+    OFPERR_OFPPMFC_EPERM,
+
 /* ## ---------------------- ## */
 /* ## OFPET_TABLE_MOD_FAILED ## */
 /* ## ---------------------- ## */
@@ -358,6 +415,9 @@ enum ofperr {
     /* OF1.1+(8,1).  Specified config is invalid. */
     OFPERR_OFPTMFC_BAD_CONFIG,
 
+    /* OF1.2+(8,2).  Permissions error. */
+    OFPERR_OFPTMFC_EPERM,
+
 /* ## --------------------- ## */
 /* ## OFPET_QUEUE_OP_FAILED ## */
 /* ## --------------------- ## */
@@ -386,12 +446,41 @@ enum ofperr {
 
     /* OF1.1+(10,1).  Specified len is invalid. */
     OFPERR_OFPSCFC_BAD_LEN,
+
+    /* OF1.2+(10,2).  Permissions error. */
+    OFPERR_OFPSCFC_EPERM,
+
+/* ## ------------------------- ## */
+/* ## OFPET_ROLE_REQUEST_FAILED ## */
+/* ## ------------------------- ## */
+
+    /* OF1.2+(11).  Controller Role request failed. */
+    OFPERR_OFPET_ROLE_REQUEST_FAILED,
+
+    /* OF1.2+(11,0).  Stale Message: old generation_id. */
+    OFPERR_OFPRRFC_STALE,
+
+    /* OF1.2+(11,1).  Controller role change unsupported. */
+    OFPERR_OFPRRFC_UNSUP,
+
+    /* NX1.0(1,513), NX1.1(1,513), OF1.2+(11,2).  Invalid role. */
+    OFPERR_OFPRRFC_BAD_ROLE,
+
+
+/* ## ------------------ ## */
+/* ## OFPET_EXPERIMENTER ## */
+/* ## ------------------ ## */
+
+    /* OF1.2+(0xffff).  Experimenter error messages. */
+    OFPERR_OFPET_EXPERIMENTER,
 };
 
 extern const struct ofperr_domain ofperr_of10;
 extern const struct ofperr_domain ofperr_of11;
+extern const struct ofperr_domain ofperr_of12;
 
 const struct ofperr_domain *ofperr_domain_from_version(uint8_t version);
+const char *ofperr_domain_get_name(const struct ofperr_domain *);
 
 bool ofperr_is_valid(enum ofperr);
 bool ofperr_is_category(enum ofperr);
@@ -401,11 +490,14 @@ 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_from_name(const char *);
 
 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 *);
+int ofperr_get_type(enum ofperr, const struct ofperr_domain *);
+int ofperr_get_code(enum ofperr, const struct ofperr_domain *);
 
 const char *ofperr_get_name(enum ofperr);
 const char *ofperr_get_description(enum ofperr);
diff --git a/ofproto/ofproto.c b/ofproto/ofproto.c
index aa97124..e7e0401 100644
--- a/ofproto/ofproto.c
+++ b/ofproto/ofproto.c
@@ -3064,7 +3064,7 @@ handle_role_request(struct ofconn *ofconn, const struct ofp_header *oh)
     role = ntohl(nrr->role);
     if (role != NX_ROLE_OTHER && role != NX_ROLE_MASTER
         && role != NX_ROLE_SLAVE) {
-        return OFPERR_NXBRC_BAD_ROLE;
+        return OFPERR_OFPRRFC_BAD_ROLE;
     }
 
     if (ofconn_get_role(ofconn) != role
diff --git a/tests/automake.mk b/tests/automake.mk
index 7728157..62f0c49 100644
--- a/tests/automake.mk
+++ b/tests/automake.mk
@@ -15,6 +15,7 @@ TESTSUITE_AT = \
 	tests/daemon.at \
 	tests/daemon-py.at \
 	tests/ofp-print.at \
+	tests/ofp-errors.at \
 	tests/ovs-ofctl.at \
 	tests/odp.at \
 	tests/multipath.at \
diff --git a/tests/ofp-errors.at b/tests/ofp-errors.at
new file mode 100644
index 0000000..5f76294
--- /dev/null
+++ b/tests/ofp-errors.at
@@ -0,0 +1,97 @@
+AT_BANNER([ofp-errors tests])
+
+AT_SETUP([OFPT_ERROR with type OFPET_HELLO_FAILED - OF1.0])
+AT_KEYWORDS([ofp-print ofp-errors])
+AT_CHECK([ovs-ofctl ofp-print 010100170000000000000001657874726120646174610a], [0], [dnl
+OFPT_ERROR (xid=0x0): OFPHFC_EPERM
+extra data\012
+])
+AT_CLEANUP
+
+AT_SETUP([OFPT_ERROR with type OFPET_HELLO_FAILED - OF1.1])
+AT_KEYWORDS([ofp-print ofp-errors])
+AT_CHECK([ovs-ofctl ofp-print 020100170000000000000001657874726120646174610a], [0], [dnl
+OFPT_ERROR (OF1.1) (xid=0x0): OFPHFC_EPERM
+extra data\012
+])
+AT_CLEANUP
+
+AT_SETUP([OFPT_ERROR with type OFPET_BAD_REQUEST - OF1.0])
+AT_KEYWORDS([ofp-print ofp-errors])
+AT_CHECK([ovs-ofctl ofp-print 01010014000000000001000601bbccddeeff0011], [0], [dnl
+OFPT_ERROR (xid=0x0): OFPBRC_BAD_LEN
+(***truncated to 8 bytes from 52445***)
+00000000  01 bb cc dd ee ff 00 11-                        |........        |
+])
+AT_CLEANUP
+
+AT_SETUP([OFPT_ERROR with code OFPBMC_BAD_PREREQ - OF1.0])
+AT_KEYWORDS([ofp-print ofp-errors])
+AT_CHECK([ovs-ofctl ofp-print '0101001c55555555 b0c20000 0000232000010104 0102000811111111'], [0], [dnl
+OFPT_ERROR (xid=0x55555555): OFPBMC_BAD_PREREQ
+OFPT_ECHO_REQUEST (xid=0x11111111): 0 bytes of payload
+])
+AT_CLEANUP
+
+AT_SETUP([OFPT_ERROR with code OFPBMC_BAD_PREREQ - OF1.1])
+AT_KEYWORDS([ofp-print ofp-errors])
+AT_CHECK([ovs-ofctl ofp-print '0201001c55555555 b0c20000 0000232000010104 0102000811111111'], [0], [dnl
+OFPT_ERROR (OF1.1) (xid=0x55555555): OFPBMC_BAD_PREREQ
+OFPT_ECHO_REQUEST (xid=0x11111111): 0 bytes of payload
+])
+AT_CLEANUP
+
+dnl Error type 3, code 1 is OFPFMFC_OVERLAP in OF1.0
+dnl and OFPBIC_UNSUP_INST in OF1.1, so check that value in both versions.
+AT_SETUP([OFPT_ERROR with type OFPFMFC_OVERLAP - OF1.0])
+AT_KEYWORDS([ofp-print ofp-errors])
+AT_CHECK([ovs-ofctl ofp-print 01010014000000000003000101bbccddeeff0011], [0], [dnl
+OFPT_ERROR (xid=0x0): OFPFMFC_OVERLAP
+(***truncated to 8 bytes from 52445***)
+00000000  01 bb cc dd ee ff 00 11-                        |........        |
+])
+AT_CLEANUP
+AT_SETUP([OFPT_ERROR with type OFPBIC_UNSUP_INST - OF1.1])
+AT_KEYWORDS([ofp-print ofp-errors])
+AT_CHECK([ovs-ofctl ofp-print 02010014000000000003000102bbccddeeff0011], [0], [dnl
+OFPT_ERROR (OF1.1) (xid=0x0): OFPBIC_UNSUP_INST
+(***truncated to 8 bytes from 52445***)
+00000000  02 bb cc dd ee ff 00 11-                        |........        |
+])
+AT_CLEANUP
+
+dnl OF1.1 had OFPBIC_UNSUP_EXP_INST as 3,5.
+dnl OF1.2 broke it into OFPBIC_BAD_EXPERIMENTER as 3,5
+dnl                 and OFPBIC_BAD_EXT_TYPE as 3,6.
+dnl Thus, for OF1.1 we translate both of the latter error codes into 3,5.
+AT_SETUP([encoding OFPBIC_* experimenter errors])
+AT_KEYWORDS([ofp-print ofp-errors])
+AT_CHECK([ovs-ofctl print-error OFPBIC_BAD_EXPERIMENTER], [0], [dnl
+OpenFlow 1.0: -1,-1
+OpenFlow 1.1: 3,5
+OpenFlow 1.2: 3,5
+])
+AT_CHECK([ovs-ofctl print-error OFPBIC_BAD_EXP_TYPE], [0], [dnl
+OpenFlow 1.0: -1,-1
+OpenFlow 1.1: 3,5
+OpenFlow 1.2: 3,6
+])
+AT_CLEANUP
+
+AT_SETUP([decoding OFPBIC_* experimenter errors])
+AT_KEYWORDS([ofp-print ofp-errors])
+AT_CHECK([ovs-ofctl ofp-print '0201001455555555 00030005 0102000811111111'], [0], [dnl
+OFPT_ERROR (OF1.1) (xid=0x55555555): OFPBIC_BAD_EXPERIMENTER
+OFPT_ECHO_REQUEST (xid=0x11111111): 0 bytes of payload
+])
+AT_KEYWORDS([ofp-print ofp-errors])
+AT_CHECK([ovs-ofctl ofp-print '0301001455555555 00030005 0102000811111111'], [0], [dnl
+OFPT_ERROR (OF 0x03) (xid=0x55555555): OFPBIC_BAD_EXPERIMENTER
+OFPT_ECHO_REQUEST (xid=0x11111111): 0 bytes of payload
+])
+AT_KEYWORDS([ofp-print ofp-errors])
+AT_CHECK([ovs-ofctl ofp-print '0301001455555555 00030006 0102000811111111'], [0], [dnl
+OFPT_ERROR (OF 0x03) (xid=0x55555555): OFPBIC_BAD_EXP_TYPE
+OFPT_ECHO_REQUEST (xid=0x11111111): 0 bytes of payload
+])
+AT_CLEANUP
diff --git a/tests/ofp-print.at b/tests/ofp-print.at
index 9ac1d23..31d3293 100644
--- a/tests/ofp-print.at
+++ b/tests/ofp-print.at
@@ -57,65 +57,7 @@ OFPT_HELLO (xid=0x0):
 ])
 AT_CLEANUP
 
-AT_SETUP([OFPT_ERROR with type OFPET_HELLO_FAILED - OF1.0])
-AT_KEYWORDS([ofp-print])
-AT_CHECK([ovs-ofctl ofp-print 010100170000000000000001657874726120646174610a], [0], [dnl
-OFPT_ERROR (xid=0x0): OFPHFC_EPERM
-extra data\012
-])
-AT_CLEANUP
-
-AT_SETUP([OFPT_ERROR with type OFPET_HELLO_FAILED - OF1.1])
-AT_KEYWORDS([ofp-print])
-AT_CHECK([ovs-ofctl ofp-print 020100170000000000000001657874726120646174610a], [0], [dnl
-OFPT_ERROR (OF1.1) (xid=0x0): OFPHFC_EPERM
-extra data\012
-])
-AT_CLEANUP
-
-AT_SETUP([OFPT_ERROR with type OFPET_BAD_REQUEST - OF1.0])
-AT_KEYWORDS([ofp-print])
-AT_CHECK([ovs-ofctl ofp-print 01010014000000000001000601bbccddeeff0011], [0], [dnl
-OFPT_ERROR (xid=0x0): OFPBRC_BAD_LEN
-(***truncated to 8 bytes from 52445***)
-00000000  01 bb cc dd ee ff 00 11-                        |........        |
-])
-AT_CLEANUP
-
-AT_SETUP([OFPT_ERROR with code NXBRC_NXM_BAD_PREREQ - OF1.0])
-AT_KEYWORDS([ofp-print])
-AT_CHECK([ovs-ofctl ofp-print '0101001c55555555 b0c20000 0000232000010104 0102000811111111'], [0], [dnl
-OFPT_ERROR (xid=0x55555555): NXBRC_NXM_BAD_PREREQ
-OFPT_ECHO_REQUEST (xid=0x11111111): 0 bytes of payload
-])
-AT_CLEANUP
-
-AT_SETUP([OFPT_ERROR with code NXBRC_NXM_BAD_PREREQ - OF1.1])
-AT_KEYWORDS([ofp-print])
-AT_CHECK([ovs-ofctl ofp-print '0201001c55555555 b0c20000 0000232000010104 0102000811111111'], [0], [dnl
-OFPT_ERROR (OF1.1) (xid=0x55555555): NXBRC_NXM_BAD_PREREQ
-OFPT_ECHO_REQUEST (xid=0x11111111): 0 bytes of payload
-])
-AT_CLEANUP
-
-dnl Error type 3, code 1 is OFPFMFC_OVERLAP in OF1.0
-dnl and OFPBIC_UNSUP_INST in OF1.1, so check that value in both versions.
-AT_SETUP([OFPT_ERROR with type OFPFMFC_OVERLAP - OF1.0])
-AT_KEYWORDS([ofp-print])
-AT_CHECK([ovs-ofctl ofp-print 01010014000000000003000101bbccddeeff0011], [0], [dnl
-OFPT_ERROR (xid=0x0): OFPFMFC_OVERLAP
-(***truncated to 8 bytes from 52445***)
-00000000  01 bb cc dd ee ff 00 11-                        |........        |
-])
-AT_CLEANUP
-AT_SETUP([OFPT_ERROR with type OFPBIC_UNSUP_INST - OF1.1])
-AT_KEYWORDS([ofp-print])
-AT_CHECK([ovs-ofctl ofp-print 02010014000000000003000102bbccddeeff0011], [0], [dnl
-OFPT_ERROR (OF1.1) (xid=0x0): OFPBIC_UNSUP_INST
-(***truncated to 8 bytes from 52445***)
-00000000  02 bb cc dd ee ff 00 11-                        |........        |
-])
-AT_CLEANUP
+dnl OFPT_ERROR tests are in ofp-errors.at.
 
 AT_SETUP([OFPT_ECHO_REQUEST, empty payload])
 AT_KEYWORDS([ofp-print])
diff --git a/tests/ovs-ofctl.at b/tests/ovs-ofctl.at
index 0395925..a52382e 100644
--- a/tests/ovs-ofctl.at
+++ b/tests/ovs-ofctl.at
@@ -433,7 +433,7 @@ NXM_OF_IN_PORT(0012), NXM_OF_ETH_TYPE(0800)
 
 # vlan tci
 NXM_OF_VLAN_TCI(f009)
-nx_pull_match() returned error NXBRC_NXM_DUP_TYPE
+nx_pull_match() returned error OFPBMC_DUP_FIELD
 NXM_OF_VLAN_TCI(0000)
 NXM_OF_VLAN_TCI(3123)
 NXM_OF_VLAN_TCI(0123)
@@ -443,118 +443,118 @@ NXM_OF_VLAN_TCI_W(0000/e000)
 
 # IP TOS
 NXM_OF_ETH_TYPE(0800), NXM_OF_IP_TOS(f0)
-nx_pull_match() returned error NXBRC_NXM_BAD_VALUE
-nx_pull_match() returned error NXBRC_NXM_BAD_PREREQ
+nx_pull_match() returned error OFPBMC_BAD_VALUE
+nx_pull_match() returned error OFPBMC_BAD_PREREQ
 
 # IP ECN
 NXM_OF_ETH_TYPE(0800), NXM_NX_IP_ECN(03)
-nx_pull_match() returned error NXBRC_NXM_BAD_VALUE
-nx_pull_match() returned error NXBRC_NXM_BAD_PREREQ
+nx_pull_match() returned error OFPBMC_BAD_VALUE
+nx_pull_match() returned error OFPBMC_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 NXBRC_NXM_BAD_PREREQ
+nx_pull_match() returned error OFPBMC_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 NXBRC_NXM_BAD_PREREQ
+nx_pull_match() returned error OFPBMC_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 NXBRC_NXM_BAD_PREREQ
-nx_pull_match() returned error NXBRC_NXM_BAD_PREREQ
+nx_pull_match() returned error OFPBMC_BAD_PREREQ
+nx_pull_match() returned error OFPBMC_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 NXBRC_NXM_BAD_PREREQ
-nx_pull_match() returned error NXBRC_NXM_BAD_PREREQ
+nx_pull_match() returned error OFPBMC_BAD_PREREQ
+nx_pull_match() returned error OFPBMC_BAD_PREREQ
 
 # TCP source port
 NXM_OF_ETH_TYPE(0800), NXM_OF_IP_PROTO(06), NXM_OF_TCP_SRC(4231)
 NXM_OF_ETH_TYPE(0800), NXM_OF_IP_PROTO(06), NXM_OF_TCP_SRC_W(5050/f0f0)
-nx_pull_match() returned error NXBRC_NXM_BAD_PREREQ
+nx_pull_match() returned error OFPBMC_BAD_PREREQ
 
 # TCP destination port
 NXM_OF_ETH_TYPE(0800), NXM_OF_IP_PROTO(06), NXM_OF_TCP_DST(4231)
 NXM_OF_ETH_TYPE(0800), NXM_OF_IP_PROTO(06), NXM_OF_TCP_DST_W(fde0/fff0)
-nx_pull_match() returned error NXBRC_NXM_BAD_PREREQ
+nx_pull_match() returned error OFPBMC_BAD_PREREQ
 
 # UDP source port
 NXM_OF_ETH_TYPE(0800), NXM_OF_IP_PROTO(11), NXM_OF_UDP_SRC(8732)
 NXM_OF_ETH_TYPE(0800), NXM_OF_IP_PROTO(11), NXM_OF_UDP_SRC_W(0132/01ff)
-nx_pull_match() returned error NXBRC_NXM_BAD_PREREQ
+nx_pull_match() returned error OFPBMC_BAD_PREREQ
 
 # UDP destination port
 NXM_OF_ETH_TYPE(0800), NXM_OF_IP_PROTO(11), NXM_OF_UDP_DST(1782)
 NXM_OF_ETH_TYPE(0800), NXM_OF_IP_PROTO(11), NXM_OF_UDP_DST_W(5005/f00f)
-nx_pull_match() returned error NXBRC_NXM_BAD_PREREQ
+nx_pull_match() returned error OFPBMC_BAD_PREREQ
 
 # ICMP type
 NXM_OF_ETH_TYPE(0800), NXM_OF_IP_PROTO(01), NXM_OF_ICMP_TYPE(12)
-nx_pull_match() returned error NXBRC_NXM_BAD_PREREQ
+nx_pull_match() returned error OFPBMC_BAD_PREREQ
 
 # ICMP code
 NXM_OF_ETH_TYPE(0800), NXM_OF_IP_PROTO(01), NXM_OF_ICMP_CODE(12)
-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
+nx_pull_match() returned error OFPBMC_BAD_PREREQ
+nx_pull_match() returned error OFPBMC_BAD_PREREQ
+nx_pull_match() returned error OFPBMC_BAD_PREREQ
 
 # ARP opcode
 NXM_OF_ETH_TYPE(0806), NXM_OF_ARP_OP(0001)
-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
+nx_pull_match() returned error OFPBMC_BAD_VALUE
+nx_pull_match() returned error OFPBMC_BAD_PREREQ
+nx_pull_match() returned error OFPBMC_BAD_PREREQ
+nx_pull_match() returned error OFPBMC_DUP_FIELD
 
 # 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 NXBRC_NXM_BAD_PREREQ
-nx_pull_match() returned error NXBRC_NXM_BAD_PREREQ
+nx_pull_match() returned error OFPBMC_BAD_PREREQ
+nx_pull_match() returned error OFPBMC_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 NXBRC_NXM_BAD_PREREQ
-nx_pull_match() returned error NXBRC_NXM_BAD_PREREQ
+nx_pull_match() returned error OFPBMC_BAD_PREREQ
+nx_pull_match() returned error OFPBMC_BAD_PREREQ
 
 # ARP source hardware address
 NXM_OF_ETH_TYPE(0806), NXM_NX_ARP_SHA(0002e30f80a4)
-nx_pull_match() returned error NXBRC_NXM_BAD_PREREQ
-nx_pull_match() returned error NXBRC_NXM_BAD_PREREQ
+nx_pull_match() returned error OFPBMC_BAD_PREREQ
+nx_pull_match() returned error OFPBMC_BAD_PREREQ
 
 # ARP destination hardware address
 NXM_OF_ETH_TYPE(0806), NXM_NX_ARP_THA(0002e30f80a4)
-nx_pull_match() returned error NXBRC_NXM_BAD_PREREQ
-nx_pull_match() returned error NXBRC_NXM_BAD_PREREQ
+nx_pull_match() returned error OFPBMC_BAD_PREREQ
+nx_pull_match() returned error OFPBMC_BAD_PREREQ
 
 # IPv6 source
 NXM_OF_ETH_TYPE(86dd), NXM_NX_IPV6_SRC(20010db83c4d00010002000300040005)
-nx_pull_match() returned error NXBRC_NXM_BAD_PREREQ
+nx_pull_match() returned error OFPBMC_BAD_PREREQ
 NXM_OF_ETH_TYPE(86dd), NXM_NX_IPV6_SRC_W(20010db83c4d00010000000000000000/ffffffffffffffff0000000000000000)
-nx_pull_match() returned error NXBRC_NXM_BAD_PREREQ
+nx_pull_match() returned error OFPBMC_BAD_PREREQ
 
 # IPv6 destination
 NXM_OF_ETH_TYPE(86dd), NXM_NX_IPV6_DST(20010db83c4d00010002000300040005)
-nx_pull_match() returned error NXBRC_NXM_BAD_PREREQ
+nx_pull_match() returned error OFPBMC_BAD_PREREQ
 NXM_OF_ETH_TYPE(86dd), NXM_NX_IPV6_DST_W(20010db83c4d00010000000000000000/ffffffffffffffff0000000000000000)
-nx_pull_match() returned error NXBRC_NXM_BAD_PREREQ
+nx_pull_match() returned error OFPBMC_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 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 OFPBMC_BAD_PREREQ
+nx_pull_match() returned error OFPBMC_BAD_PREREQ
+nx_pull_match() returned error OFPBMC_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 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 OFPBMC_BAD_PREREQ
+nx_pull_match() returned error OFPBMC_BAD_PREREQ
+nx_pull_match() returned error OFPBMC_BAD_PREREQ
 
 # IPv4 fragments.
 NXM_OF_ETH_TYPE(0800), NXM_NX_IP_FRAG(00)
@@ -567,7 +567,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 NXBRC_NXM_BAD_VALUE
+nx_pull_match() returned error OFPBMC_BAD_VALUE
 
 # IPv6 fragments.
 NXM_OF_ETH_TYPE(86dd), NXM_NX_IP_FRAG(00)
@@ -580,7 +580,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 NXBRC_NXM_BAD_VALUE
+nx_pull_match() returned error OFPBMC_BAD_VALUE
 
 # Flow cookie.
 NXM_NX_COOKIE(00000000abcdef01)
@@ -595,7 +595,7 @@ NXM_NX_REG0(acebdf56)
 NXM_NX_REG0_W(a0e0d050/f0f0f0f0)
 
 # Invalid field number.
-nx_pull_match() returned error NXBRC_NXM_BAD_TYPE
+nx_pull_match() returned error OFPBMC_BAD_FIELD
 
 # Unimplemented registers.
 #
@@ -603,8 +603,8 @@ nx_pull_match() returned error NXBRC_NXM_BAD_TYPE
 # registers are implemented.
 NXM_NX_REG0(12345678)
 NXM_NX_REG0_W(12345678/12345678)
-nx_pull_match() returned error NXBRC_NXM_BAD_TYPE
-nx_pull_match() returned error NXBRC_NXM_BAD_TYPE
+nx_pull_match() returned error OFPBMC_BAD_FIELD
+nx_pull_match() returned error OFPBMC_BAD_FIELD
 ])
 AT_CLEANUP
 
@@ -615,7 +615,7 @@ NXM_OF_IN_PORT(0001), 01020304(1111/2222), NXM_OF_ETH_TYPE(0800)
 ])
 
 AT_CHECK([ovs-ofctl --strict parse-nx-match < nx-match.txt], [0], [dnl
-nx_pull_match() returned error NXBRC_NXM_BAD_TYPE
+nx_pull_match() returned error OFPBMC_BAD_FIELD
 ])
 
 AT_CHECK([ovs-ofctl parse-nx-match < nx-match.txt], [0], [dnl
diff --git a/tests/testsuite.at b/tests/testsuite.at
index 755462c..60401d6 100644
--- a/tests/testsuite.at
+++ b/tests/testsuite.at
@@ -57,6 +57,7 @@ m4_include([tests/check-structs.at])
 m4_include([tests/daemon.at])
 m4_include([tests/daemon-py.at])
 m4_include([tests/ofp-print.at])
+m4_include([tests/ofp-errors.at])
 m4_include([tests/ovs-ofctl.at])
 m4_include([tests/odp.at])
 m4_include([tests/multipath.at])
diff --git a/utilities/ovs-ofctl.c b/utilities/ovs-ofctl.c
index 74a86c1..3e9f462 100644
--- a/utilities/ovs-ofctl.c
+++ b/utilities/ovs-ofctl.c
@@ -1832,6 +1832,34 @@ do_parse_nx_match(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
     ds_destroy(&in);
 }
 
+/* "print-error ENUM": Prints the type and code of ENUM for every OpenFlow
+ * version. */
+static void
+do_print_error(int argc OVS_UNUSED, char *argv[])
+{
+    enum ofperr error;
+    int version;
+
+    error = ofperr_from_name(argv[1]);
+    if (!error) {
+        ovs_fatal(0, "unknown error \"%s\"", argv[1]);
+    }
+
+    for (version = 0; version <= UINT8_MAX; version++) {
+        const struct ofperr_domain *domain;
+
+        domain = ofperr_domain_from_version(version);
+        if (!domain) {
+            continue;
+        }
+
+        printf("%s: %d,%d\n",
+               ofperr_domain_get_name(domain),
+               ofperr_get_type(error, domain),
+               ofperr_get_code(error, domain));
+    }
+}
+
 /* "ofp-print HEXSTRING [VERBOSITY]": Converts the hex digits in HEXSTRING into
  * binary data, interpreting them as an OpenFlow message, and prints the
  * OpenFlow message on stdout, at VERBOSITY (level 2 by default).  */
@@ -1877,6 +1905,7 @@ static const struct command all_commands[] = {
     { "parse-flow", 1, 1, do_parse_flow },
     { "parse-flows", 1, 1, do_parse_flows },
     { "parse-nx-match", 0, 0, do_parse_nx_match },
+    { "print-error", 1, 1, do_print_error },
     { "ofp-print", 1, 2, do_ofp_print },
 
     { NULL, 0, 0, NULL },
-- 
1.7.2.5




More information about the dev mailing list