summaryrefslogtreecommitdiff
path: root/build-aux/extract-ofp-actions
diff options
context:
space:
mode:
Diffstat (limited to 'build-aux/extract-ofp-actions')
-rwxr-xr-xbuild-aux/extract-ofp-actions376
1 files changed, 376 insertions, 0 deletions
diff --git a/build-aux/extract-ofp-actions b/build-aux/extract-ofp-actions
new file mode 100755
index 000000000..28ecfeabc
--- /dev/null
+++ b/build-aux/extract-ofp-actions
@@ -0,0 +1,376 @@
+#! /usr/bin/python
+
+import sys
+import os.path
+import re
+
+OFP_ACTION_ALIGN = 8
+
+# Map from OpenFlow version number to version ID used in ofp_header.
+version_map = {"1.0": 0x01,
+ "1.1": 0x02,
+ "1.2": 0x03,
+ "1.3": 0x04,
+ "1.4": 0x05,
+ "1.5": 0x06}
+version_reverse_map = dict((v, k) for (k, v) in version_map.iteritems())
+
+# Map from vendor name to the length of the action header.
+vendor_map = {"OF": (0x00000000, 4),
+ "NX": (0x00002320, 10)}
+
+# Basic types used in action arguments.
+types = {}
+types['uint8_t'] = {"size": 1, "align": 1, "ntoh": None, "hton": None}
+types['ovs_be16'] = {"size": 2, "align": 2, "ntoh": "ntohs", "hton": "htons"}
+types['ovs_be32'] = {"size": 4, "align": 4, "ntoh": "ntohl", "hton": "htonl"}
+types['ovs_be64'] = {"size": 8, "align": 8, "ntoh": "ntohll", "hton": "htonll"}
+types['uint16_t'] = {"size": 2, "align": 2, "ntoh": None, "hton": None}
+types['uint32_t'] = {"size": 4, "align": 4, "ntoh": None, "hton": None}
+types['uint64_t'] = {"size": 8, "align": 8, "ntoh": None, "hton": None}
+
+line = ""
+
+arg_structs = set()
+
+def round_up(x, y):
+ return (x + (y - 1)) / y * y
+
+def open_file(fn):
+ global file_name
+ global input_file
+ global line_number
+ file_name = fn
+ input_file = open(file_name)
+ line_number = 0
+
+def get_line():
+ global input_file
+ global line
+ global line_number
+ line = input_file.readline()
+ line_number += 1
+ if line == "":
+ fatal("unexpected end of input")
+ return line
+
+n_errors = 0
+def error(msg):
+ global n_errors
+ sys.stderr.write("%s:%d: %s\n" % (file_name, line_number, msg))
+ n_errors += 1
+
+def fatal(msg):
+ error(msg)
+ sys.exit(1)
+
+def usage():
+ argv0 = os.path.basename(sys.argv[0])
+ print ('''\
+%(argv0)s, for extracting OpenFlow action data
+usage: %(argv0)s OFP_ACTIONS.C [--prototypes | --definitions]
+
+This program reads ofp-actions.c to obtain information about OpenFlow
+actions. With --prototypes, it outputs on stdout a set of prototypes to
+#include early in ofp-actions.c. With --definitions, it outputs on stdout
+a set of definitions to #include late in ofp-actions.c
+
+OFP_ACTIONS.C should point to lib/ofp-actions.c.\
+''' % {"argv0": argv0})
+ sys.exit(0)
+
+def extract_ofp_actions(fn, definitions):
+ error_types = {}
+
+ comments = []
+ names = []
+ domain = {}
+ for code, size in vendor_map.values():
+ domain[code] = {}
+ enums = {}
+
+ n_errors = 0
+
+ open_file(fn)
+
+ while True:
+ get_line()
+ if re.match('enum ofp_raw_action_type {', line):
+ break
+
+ while True:
+ get_line()
+ if line.startswith('/*') or not line or line.isspace():
+ continue
+ elif re.match('}', line):
+ break
+
+ if not line.lstrip().startswith('/*'):
+ fatal("unexpected syntax between actions")
+
+ comment = line.lstrip()[2:].strip()
+ while not comment.endswith('*/'):
+ get_line()
+ if line.startswith('/*') or not line or line.isspace():
+ fatal("unexpected syntax within action")
+ comment += ' %s' % line.lstrip('* \t').rstrip(' \t\r\n')
+ comment = re.sub('\[[^]]*\]', '', comment)
+ comment = comment[:-2].rstrip()
+
+ m = re.match('([^:]+):\s+(.*)$', comment)
+ if not m:
+ fatal("unexpected syntax between actions")
+
+ dsts = m.group(1)
+ argtype = m.group(2).strip().replace('.', '', 1)
+
+ get_line()
+ m = re.match(r'\s+(([A-Z]+)_RAW([0-9]*)_([A-Z0-9_]+)),?', line)
+ if not m:
+ fatal("syntax error expecting enum value")
+
+ enum = m.group(1)
+ if enum in names:
+ fatal("%s specified twice" % enum)
+
+ names.append(enum)
+
+ for dst in dsts.split(', '):
+ m = re.match(r'([A-Z]+)([0-9.]+)(\+|-[0-9.]+)?(?:\((\d+)\))(?: is deprecated \(([^)]+)\))?$', dst)
+ if not m:
+ fatal("%r: syntax error in destination" % dst)
+ vendor_name = m.group(1)
+ version1_name = m.group(2)
+ version2_name = m.group(3)
+ type_ = int(m.group(4))
+ deprecation = m.group(5)
+
+ if vendor_name not in vendor_map:
+ fatal("%s: unknown vendor" % vendor_name)
+ vendor = vendor_map[vendor_name][0]
+
+ if version1_name not in version_map:
+ fatal("%s: unknown OpenFlow version" % version1_name)
+ v1 = version_map[version1_name]
+
+ if version2_name is None:
+ v2 = v1
+ elif version2_name == "+":
+ v2 = max(version_map.values())
+ elif version2_name[1:] not in version_map:
+ fatal("%s: unknown OpenFlow version" % version2_name[1:])
+ else:
+ v2 = version_map[version2_name[1:]]
+
+ if v2 < v1:
+ fatal("%s%s: %s precedes %s"
+ % (version1_name, version2_name,
+ version2_name, version1_name))
+
+ for version in range(v1, v2 + 1):
+ domain[vendor].setdefault(type_, {})
+ if version in domain[vendor][type_]:
+ v = domain[vendor][type_][version]
+ msg = "%#x,%d in OF%s means both %s and %s" % (
+ vendor, type_, version_reverse_map[version],
+ v["enum"], enum)
+ error("%s: %s." % (dst, msg))
+ sys.stderr.write("%s:%d: %s: Here is the location "
+ "of the previous definition.\n"
+ % (v["file_name"], v["line_number"],
+ dst))
+ n_errors += 1
+ else:
+ header_len = vendor_map[vendor_name][1]
+
+ base_argtype = argtype.replace(', ..', '', 1)
+ if base_argtype in types:
+ arg_align = types[base_argtype]['align']
+ arg_len = types[base_argtype]['size']
+ arg_ofs = round_up(header_len, arg_align)
+ min_length = round_up(arg_ofs + arg_len,
+ OFP_ACTION_ALIGN)
+ elif base_argtype == 'void':
+ min_length = round_up(header_len, OFP_ACTION_ALIGN)
+ arg_len = 0
+ arg_ofs = 0
+ elif re.match(r'struct [a-zA-Z0-9_]+$', base_argtype):
+ min_length = 'sizeof(%s)' % base_argtype
+ arg_structs.add(base_argtype)
+ arg_len = 0
+ arg_ofs = 0
+ # should also emit OFP_ACTION_ALIGN assertion
+ else:
+ fatal("bad argument type %s" % argtype)
+
+ ellipsis = argtype != base_argtype
+ if ellipsis:
+ max_length = '65536 - OFP_ACTION_ALIGN'
+ else:
+ max_length = min_length
+
+ info = {"enum": enum, # 0
+ "deprecation": deprecation, # 1
+ "file_name": file_name, # 2
+ "line_number": line_number, # 3
+ "min_length": min_length, # 4
+ "max_length": max_length, # 5
+ "arg_ofs": arg_ofs, # 6
+ "arg_len": arg_len, # 7
+ "base_argtype": base_argtype, # 8
+ "version": version, # 9
+ "type": type_} # 10
+ domain[vendor][type_][version] = info
+
+ enums.setdefault(enum, [])
+ enums[enum].append(info)
+
+ input_file.close()
+
+ if n_errors:
+ sys.exit(1)
+
+ print """\
+/* Generated automatically; do not modify! -*- buffer-read-only: t -*- */
+"""
+
+ if definitions:
+ print "/* Verify that structs used as actions are reasonable sizes. */"
+ for s in sorted(arg_structs):
+ print "BUILD_ASSERT_DECL(sizeof(%s) %% OFP_ACTION_ALIGN == 0);" % s
+
+ print "\nstatic struct ofpact_raw_instance all_raw_instances[] = {"
+ for vendor in domain:
+ for type_ in domain[vendor]:
+ for version in domain[vendor][type_]:
+ d = domain[vendor][type_][version]
+ print " { { 0x%08x, %2d, 0x%02x }, " % (
+ vendor, type_, version)
+ print " %s," % d["enum"]
+ print " HMAP_NODE_NULL_INITIALIZER,"
+ print " HMAP_NODE_NULL_INITIALIZER,"
+ print " %s," % d["min_length"]
+ print " %s," % d["max_length"]
+ print " %s," % d["arg_ofs"]
+ print " %s," % d["arg_len"]
+ print " \"%s\"," % re.sub('_RAW[0-9]*', '', d["enum"], 1)
+ if d["deprecation"]:
+ print " \"%s\"," % re.sub(r'(["\\])', r'\\\1', d["deprecation"])
+ else:
+ print " NULL,"
+ print " },"
+ print "};";
+
+ for versions in enums.values():
+ need_ofp_version = False
+ for v in versions:
+ assert v["arg_len"] == versions[0]["arg_len"]
+ assert v["base_argtype"] == versions[0]["base_argtype"]
+ if (v["min_length"] != versions[0]["min_length"] or
+ v["arg_ofs"] != versions[0]["arg_ofs"] or
+ v["type"] != versions[0]["type"]):
+ need_ofp_version = True
+ base_argtype = versions[0]["base_argtype"]
+
+ decl = "static inline "
+ if base_argtype.startswith('struct'):
+ decl += "%s *" %base_argtype
+ else:
+ decl += "void"
+ decl += "\nput_%s(struct ofpbuf *openflow" % versions[0]["enum"].replace('_RAW', '', 1)
+ if need_ofp_version:
+ decl += ", enum ofp_version version"
+ if base_argtype != 'void' and not base_argtype.startswith('struct'):
+ decl += ", %s arg" % base_argtype
+ decl += ")"
+ if definitions:
+ decl += "{\n"
+ decl += " "
+ if base_argtype.startswith('struct'):
+ decl += "return "
+ decl += "ofpact_put_raw(openflow, "
+ if need_ofp_version:
+ decl += "version"
+ else:
+ decl += "%s" % versions[0]["version"]
+ decl += ", %s, " % versions[0]["enum"]
+ if base_argtype.startswith('struct') or base_argtype == 'void':
+ decl += "0"
+ else:
+ ntoh = types[base_argtype]['ntoh']
+ if ntoh:
+ decl += "%s(arg)" % ntoh
+ else:
+ decl += "arg"
+ decl += ");\n"
+ decl += "}"
+ else:
+ decl += ";"
+ print decl
+ print
+
+ if definitions:
+ print """\
+static enum ofperr
+ofpact_decode(const struct ofp_action_header *a, enum ofp_raw_action_type raw,
+ uint64_t arg, struct ofpbuf *out)
+{
+ switch (raw) {\
+"""
+ for versions in enums.values():
+ enum = versions[0]["enum"]
+ print " case %s:" % enum
+ base_argtype = versions[0]["base_argtype"]
+ if base_argtype == 'void':
+ print " return decode_%s(out);" % enum
+ else:
+ if base_argtype.startswith('struct'):
+ arg = "ALIGNED_CAST(const %s *, a)" % base_argtype
+ else:
+ hton = types[base_argtype]['hton']
+ if hton:
+ arg = "%s(arg)" % hton
+ else:
+ arg = "arg"
+ print " return decode_%s(%s, out);" % (enum, arg)
+ print
+ print """\
+ default:
+ OVS_NOT_REACHED();
+ }
+}\
+"""
+ else:
+ for versions in enums.values():
+ enum = versions[0]["enum"]
+ prototype = "static enum ofperr decode_%s(" % enum
+ base_argtype = versions[0]["base_argtype"]
+ if base_argtype != 'void':
+ if base_argtype.startswith('struct'):
+ prototype += "const %s *, " % base_argtype
+ else:
+ prototype += "%s, " % base_argtype
+ prototype += "struct ofpbuf *);"
+ print prototype
+
+ print """
+static enum ofperr ofpact_decode(const struct ofp_action_header *,
+ enum ofp_raw_action_type raw,
+ uint64_t arg, struct ofpbuf *out);
+"""
+
+if __name__ == '__main__':
+ if '--help' in sys.argv:
+ usage()
+ elif len(sys.argv) != 3:
+ sys.stderr.write("exactly two arguments required; "
+ "use --help for help\n")
+ sys.exit(1)
+ elif sys.argv[2] == '--prototypes':
+ extract_ofp_actions(sys.argv[1], False)
+ elif sys.argv[2] == '--definitions':
+ extract_ofp_actions(sys.argv[1], True)
+ else:
+ sys.stderr.write("invalid arguments; use --help for help\n")
+ sys.exit(1)
+