Automatically extract error types and codes for formatting.
authorBen Pfaff <blp@nicira.com>
Wed, 12 Jan 2011 21:42:50 +0000 (13:42 -0800)
committerBen Pfaff <blp@nicira.com>
Wed, 12 Jan 2011 21:51:43 +0000 (13:51 -0800)
build-aux/extract-ofp-errors [new file with mode: 0755]
include/openflow/nicira-ext.h
lib/.gitignore
lib/automake.mk
lib/ofp-errors.h [new file with mode: 0644]
lib/ofp-print.c
lib/ofp-util.c
lib/ofp-util.h
ofproto/ofproto.c

diff --git a/build-aux/extract-ofp-errors b/build-aux/extract-ofp-errors
new file mode 100755 (executable)
index 0000000..c34888f
--- /dev/null
@@ -0,0 +1,225 @@
+#! /usr/bin/python
+
+import sys
+import os.path
+import re
+
+macros = {}
+
+token = None
+line = ""
+idRe = "[a-zA-Z_][a-zA-Z_0-9]*"
+tokenRe = "#?" + idRe + "|[0-9]+|."
+inComment = False
+inDirective = False
+def getToken():
+    global token
+    global line
+    global inComment
+    global inDirective
+    while True:
+        line = line.lstrip()
+        if line != "":
+            if line.startswith("/*"):
+                inComment = True
+                line = line[2:]
+            elif inComment:
+                commentEnd = line.find("*/")
+                if commentEnd < 0:
+                    line = ""
+                else:
+                    inComment = False
+                    line = line[commentEnd + 2:]
+            else:
+                match = re.match(tokenRe, line)
+                token = match.group(0)
+                line = line[len(token):]
+                if token.startswith('#'):
+                    inDirective = True
+                elif token in macros and not inDirective:
+                    line = macros[token] + line
+                    continue
+                return True
+        elif inDirective:
+            token = "$"
+            inDirective = False
+            return True
+        else:
+            global lineNumber
+            line = inputFile.readline()
+            lineNumber += 1
+            while line.endswith("\\\n"):
+                line = line[:-2] + inputFile.readline()
+                lineNumber += 1
+            if line == "":
+                if token == None:
+                    fatal("unexpected end of input")
+                token = None
+                return False
+
+def fatal(msg):
+    sys.stderr.write("%s:%d: error at \"%s\": %s\n" % (fileName, lineNumber, token, msg))
+    sys.exit(1)
+
+def skipDirective():
+    getToken()
+    while token != '$':
+        getToken()
+
+def isId(s):
+    return re.match(idRe + "$", s) != None
+
+def forceId():
+    if not isId(token):
+        fatal("identifier expected")
+
+def forceInteger():
+    if not re.match('[0-9]+$', token):
+        fatal("integer expected")
+
+def match(t):
+    if token == t:
+        getToken()
+        return True
+    else:
+        return False
+
+def forceMatch(t):
+    if not match(t):
+        fatal("%s expected" % t)
+
+def parseTaggedName():
+    assert token in ('struct', 'union')
+    name = token
+    getToken()
+    forceId()
+    name = "%s %s" % (name, token)
+    getToken()
+    return name
+
+def print_enum(tag, constants, storage_class):
+    print """
+%(storage_class)sconst char *
+%(tag)s_to_string(uint16_t value)
+{
+    switch (value) {\
+""" % {"tag": tag,
+       "bufferlen": len(tag) + 32,
+       "storage_class": storage_class}
+    for constant in constants:
+        print "    case %s: return \"%s\";" % (constant, constant)
+    print """\
+    }
+    return NULL;
+}\
+""" % {"tag": tag}
+
+def usage():
+    argv0 = os.path.basename(sys.argv[0])
+    print '''\
+%(argv0)s, for extracting OpenFlow error codes from header files
+usage: %(argv0)s FILE [FILE...]
+
+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}
+    sys.exit(0)
+
+def extract_ofp_errors(filenames):
+    error_types = {}
+
+    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")
+        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 """\
+    }
+    return NULL;
+}\
+"""
+
+if __name__ == '__main__':
+    if '--help' in sys.argv:
+        usage()
+    elif len(sys.argv) < 2:
+        sys.stderr.write("at least one non-option argument required; "
+                         "use --help for help\n")
+        sys.exit(1)
+    else:
+        extract_ofp_errors(sys.argv[1:])
index 14c06f08bc0888fa7651901a732fb11a7cb0655d..0b01aaadfc482f155c6299b1968aab2a252bd701 100644 (file)
@@ -76,7 +76,7 @@ struct nx_vendor_error {
  * that duplicate the current ones' meanings. */
 
 /* Additional "code" values for OFPET_BAD_REQUEST. */
-enum {
+enum nx_bad_request_code {
 /* Nicira Extended Match (NXM) errors. */
 
     /* Generic error code used when there is an error in an NXM sent to the
@@ -103,7 +103,7 @@ enum {
 };
 
 /* Additional "code" values for OFPET_FLOW_MOD_FAILED. */
-enum {
+enum nx_flow_mod_failed_code {
     /* Generic hardware error. */
     NXFMFC_HARDWARE = 0x100,
 
index d56deecbed64a3dec4da391e9d1aa243ec857666..c5b6cac944b89302eefb32bcb9df8f724b0480e4 100644 (file)
@@ -3,3 +3,4 @@
 /dhparams.c
 /dirs.c
 /coverage-counters.c
+/ofp-errors.c
index f5d23a23b116d8328b9cded62c028557eb93b39d..269ab897f817f3d8016ee09fff0076be8c5f5033 100644 (file)
@@ -81,6 +81,8 @@ lib_libopenvswitch_a_SOURCES = \
        lib/nx-match.h \
        lib/odp-util.c \
        lib/odp-util.h \
+       lib/ofp-errors.c \
+       lib/ofp-errors.h \
        lib/ofp-parse.c \
        lib/ofp-parse.h \
        lib/ofp-print.c \
@@ -245,6 +247,14 @@ 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
+
 install-data-local: lib-install-data-local
 lib-install-data-local:
        $(MKDIR_P) $(DESTDIR)$(RUNDIR)
diff --git a/lib/ofp-errors.h b/lib/ofp-errors.h
new file mode 100644 (file)
index 0000000..d677b5d
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2008, 2009, 2010 Nicira Networks.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef OFP_ERRORS_H
+#define OFP_ERRORS_H 1
+
+#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);
+
+#endif /* ofp-errors.h */
index d88a574cbb8eac69c42e25f340725082fcf8fd58..9d1b3b0c0a5d3052171bbbe8c5a675464bebdcc4 100644 (file)
@@ -953,158 +953,52 @@ ofp_print_port_mod(struct ds *string, const struct ofp_port_mod *opm)
     }
 }
 
-struct error_type {
-    int type;
-    int code;
-    const char *name;
-};
-
-static const struct error_type error_types[] = {
-#define ERROR_TYPE(TYPE) {TYPE, -1, #TYPE}
-#define ERROR_CODE(TYPE, CODE) {TYPE, CODE, #CODE}
-    ERROR_TYPE(OFPET_HELLO_FAILED),
-    ERROR_CODE(OFPET_HELLO_FAILED, OFPHFC_INCOMPATIBLE),
-    ERROR_CODE(OFPET_HELLO_FAILED, OFPHFC_EPERM),
-
-    ERROR_TYPE(OFPET_BAD_REQUEST),
-    ERROR_CODE(OFPET_BAD_REQUEST, OFPBRC_BAD_VERSION),
-    ERROR_CODE(OFPET_BAD_REQUEST, OFPBRC_BAD_TYPE),
-    ERROR_CODE(OFPET_BAD_REQUEST, OFPBRC_BAD_STAT),
-    ERROR_CODE(OFPET_BAD_REQUEST, OFPBRC_BAD_VENDOR),
-    ERROR_CODE(OFPET_BAD_REQUEST, OFPBRC_BAD_SUBTYPE),
-    ERROR_CODE(OFPET_BAD_REQUEST, OFPBRC_EPERM),
-    ERROR_CODE(OFPET_BAD_REQUEST, OFPBRC_BAD_LEN),
-    ERROR_CODE(OFPET_BAD_REQUEST, OFPBRC_BUFFER_EMPTY),
-    ERROR_CODE(OFPET_BAD_REQUEST, OFPBRC_BUFFER_UNKNOWN),
-    /* Nicira error extensions. */
-    ERROR_CODE(OFPET_BAD_REQUEST, NXBRC_NXM_INVALID),
-    ERROR_CODE(OFPET_BAD_REQUEST, NXBRC_NXM_BAD_TYPE),
-    ERROR_CODE(OFPET_BAD_REQUEST, NXBRC_NXM_BAD_VALUE),
-    ERROR_CODE(OFPET_BAD_REQUEST, NXBRC_NXM_BAD_MASK),
-    ERROR_CODE(OFPET_BAD_REQUEST, NXBRC_NXM_BAD_PREREQ),
-    ERROR_CODE(OFPET_BAD_REQUEST, NXBRC_NXM_DUP_TYPE),
-
-    ERROR_TYPE(OFPET_BAD_ACTION),
-    ERROR_CODE(OFPET_BAD_ACTION, OFPBAC_BAD_TYPE),
-    ERROR_CODE(OFPET_BAD_ACTION, OFPBAC_BAD_LEN),
-    ERROR_CODE(OFPET_BAD_ACTION, OFPBAC_BAD_VENDOR),
-    ERROR_CODE(OFPET_BAD_ACTION, OFPBAC_BAD_VENDOR_TYPE),
-    ERROR_CODE(OFPET_BAD_ACTION, OFPBAC_BAD_OUT_PORT),
-    ERROR_CODE(OFPET_BAD_ACTION, OFPBAC_BAD_ARGUMENT),
-    ERROR_CODE(OFPET_BAD_ACTION, OFPBAC_EPERM),
-    ERROR_CODE(OFPET_BAD_ACTION, OFPBAC_TOO_MANY),
-
-    ERROR_TYPE(OFPET_FLOW_MOD_FAILED),
-    ERROR_CODE(OFPET_FLOW_MOD_FAILED, OFPFMFC_ALL_TABLES_FULL),
-    ERROR_CODE(OFPET_FLOW_MOD_FAILED, OFPFMFC_OVERLAP),
-    ERROR_CODE(OFPET_FLOW_MOD_FAILED, OFPFMFC_EPERM),
-    ERROR_CODE(OFPET_FLOW_MOD_FAILED, OFPFMFC_BAD_EMERG_TIMEOUT),
-    ERROR_CODE(OFPET_FLOW_MOD_FAILED, OFPFMFC_BAD_COMMAND),
-    /* Nicira error extenstions. */
-    ERROR_CODE(OFPET_FLOW_MOD_FAILED, NXFMFC_HARDWARE),
-    ERROR_CODE(OFPET_FLOW_MOD_FAILED, NXFMFC_BAD_TABLE_ID),
-
-    ERROR_TYPE(OFPET_PORT_MOD_FAILED),
-    ERROR_CODE(OFPET_PORT_MOD_FAILED, OFPPMFC_BAD_PORT),
-    ERROR_CODE(OFPET_PORT_MOD_FAILED, OFPPMFC_BAD_HW_ADDR)
-};
-#define N_ERROR_TYPES ARRAY_SIZE(error_types)
-
-static const char *
-lookup_error_type(int type)
-{
-    const struct error_type *t;
-
-    for (t = error_types; t < &error_types[N_ERROR_TYPES]; t++) {
-        if (t->type == type && t->code == -1) {
-            return t->name;
-        }
-    }
-    return "?";
-}
-
-static const char *
-lookup_error_code(int type, int code)
-{
-    const struct error_type *t;
-
-    for (t = error_types; t < &error_types[N_ERROR_TYPES]; t++) {
-        if (t->type == type && t->code == code) {
-            return t->name;
-        }
-    }
-    return "?";
-}
-
 static void
 ofp_print_error(struct ds *string, int error)
 {
-    int type = get_ofp_err_type(error);
-    int code = get_ofp_err_code(error);
     if (string->length) {
         ds_put_char(string, ' ');
     }
-    ds_put_format(string, " ***decode error type:%d(%s) code:%d(%s)***\n",
-                  type, lookup_error_type(type),
-                  code, lookup_error_code(type, code));
-}
-
-static void
-ofp_print_nx_error_msg(struct ds *string, const struct ofp_error_msg *oem)
-{
-    size_t len = ntohs(oem->header.length);
-    const struct nx_vendor_error *nve = (struct nx_vendor_error *)oem->data;
-    int vendor = ntohl(nve->vendor);
-    int type = ntohs(nve->type);
-    int code = ntohs(nve->code);
-
-    ds_put_format(string, " vendor:%x type:%d(%s) code:%d(%s) payload:\n",
-                  vendor,
-                  type, lookup_error_type(type),
-                  code, lookup_error_code(type, code));
-
-    ds_put_hex_dump(string, nve + 1, len - sizeof *oem - sizeof *nve,
-                    0, true);
+    ds_put_cstr(string, "***decode error: ");
+    ofputil_format_error(string, error);
+    ds_put_cstr(string, "***\n");
 }
 
 static void
 ofp_print_error_msg(struct ds *string, const struct ofp_error_msg *oem)
 {
     size_t len = ntohs(oem->header.length);
-    int type = ntohs(oem->type);
-    int code = ntohs(oem->code);
+    size_t payload_ofs, payload_len;
+    const void *payload;
+    int error;
     char *s;
 
-
-    if (type == NXET_VENDOR && code == NXVC_VENDOR_ERROR) {
-        if (len < sizeof *oem + sizeof(struct nx_vendor_error)) {
-            ds_put_format(string,
-                          "(***truncated extended error message is %zu bytes "
-                          "when it should be at least %zu***)\n",
-                          len, sizeof(struct nx_vendor_error));
-            return;
-        }
-
-        return ofp_print_nx_error_msg(string, oem);
+    error = ofputil_decode_error_msg(&oem->header, &payload_ofs);
+    if (!is_ofp_error(error)) {
+        ofp_print_error(string, error);
+        ds_put_hex_dump(string, oem->data, len - sizeof *oem, 0, true);
+        return;
     }
 
-    ds_put_format(string, " type:%d(%s) code:%d(%s) payload:\n",
-                  type, lookup_error_type(type),
-                  code, lookup_error_code(type, code));
+    ds_put_char(string, ' ');
+    ofputil_format_error(string, error);
+    ds_put_char(string, '\n');
 
-    switch (type) {
+    payload = (const uint8_t *) oem + payload_ofs;
+    payload_len = len - payload_ofs;
+    switch (get_ofp_err_type(error)) {
     case OFPET_HELLO_FAILED:
-        ds_put_printable(string, (char *) oem->data, len - sizeof *oem);
+        ds_put_printable(string, payload, payload_len);
         break;
 
     case OFPET_BAD_REQUEST:
-        s = ofp_to_string(oem->data, len - sizeof *oem, 1);
+        s = ofp_to_string(payload, payload_len, 1);
         ds_put_cstr(string, s);
         free(s);
         break;
 
     default:
-        ds_put_hex_dump(string, oem->data, len - sizeof *oem, 0, true);
+        ds_put_hex_dump(string, payload, payload_len, 0, true);
         break;
     }
 }
@@ -1569,6 +1463,9 @@ ofp_to_string__(const struct ofp_header *oh,
         break;
 
     case OFPUTIL_OFPT_HELLO:
+        ds_put_char(string, '\n');
+        ds_put_hex_dump(string, oh + 1, ntohs(oh->length) - sizeof *oh,
+                        0, true);
         break;
 
     case OFPUTIL_OFPT_ERROR:
index 9210413a1417f15d78b9e07df12b0f3623f3d932..7aea3319e727b0074ac2d77aa713e08f61e51baf 100644 (file)
@@ -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.
 
 #include <config.h>
 #include "ofp-print.h"
+#include <errno.h>
 #include <inttypes.h>
 #include <stdlib.h>
 #include "byte-order.h"
 #include "classifier.h"
+#include "dynamic-string.h"
 #include "multipath.h"
 #include "nx-match.h"
+#include "ofp-errors.h"
 #include "ofp-util.h"
 #include "ofpbuf.h"
 #include "packets.h"
@@ -2043,6 +2046,18 @@ vendor_code_to_id(uint8_t code)
     }
 }
 
+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
@@ -2051,7 +2066,7 @@ vendor_code_to_id(uint8_t code)
  *
  * Returns NULL if 'error' is not an OpenFlow error code. */
 struct ofpbuf *
-make_ofp_error_msg(int error, const struct ofp_header *oh)
+ofputil_encode_error_msg(int error, const struct ofp_header *oh)
 {
     static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
 
@@ -2124,6 +2139,102 @@ make_ofp_error_msg(int error, const struct ofp_header *oh)
     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.
  *
index 42318654b8fa0e33b5473ab8a0428d141dd1675b..5d9e8c4d020248940b7a038fce00f2b7eddc347f 100644 (file)
@@ -384,6 +384,11 @@ get_ofp_err_code(int error)
     return error & 0xffff;
 }
 
-struct ofpbuf *make_ofp_error_msg(int error, const struct ofp_header *);
+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);
 
 #endif /* ofp-util.h */
index 52a1964bfdfe3d1ffd95ed9ad8d68e07e0179da2..df5850f19d94473927311495b445f8d7ea2b9d25 100644 (file)
@@ -2515,7 +2515,7 @@ static void
 send_error_oh(const struct ofconn *ofconn, const struct ofp_header *oh,
               int error)
 {
-    struct ofpbuf *buf = make_ofp_error_msg(error, oh);
+    struct ofpbuf *buf = ofputil_encode_error_msg(error, oh);
     if (buf) {
         COVERAGE_INC(ofproto_error);
         queue_tx(buf, ofconn, ofconn->reply_counter);