Skip to content

Commit

Permalink
Better abstract OpenFlow error codes.
Browse files Browse the repository at this point in the history
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.

Signed-off-by: Ben Pfaff <[email protected]>
  • Loading branch information
blp committed Jan 12, 2012
1 parent e25c55d commit 90bf1e0
Show file tree
Hide file tree
Showing 32 changed files with 1,277 additions and 925 deletions.
243 changes: 168 additions & 75 deletions build-aux/extract-ofp-errors
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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():
Expand Down Expand Up @@ -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:
Expand Down
77 changes: 0 additions & 77 deletions include/openflow/openflow.h
Original file line number Diff line number Diff line change
Expand Up @@ -603,83 +603,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;
Expand Down
2 changes: 1 addition & 1 deletion lib/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@
/dhparams.c
/dirs.c
/coverage-counters.c
/ofp-errors.c
/ofp-errors.inc
13 changes: 6 additions & 7 deletions lib/automake.mk
Original file line number Diff line number Diff line change
Expand Up @@ -278,13 +278,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:
Expand Down
Loading

0 comments on commit 90bf1e0

Please sign in to comment.