379 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			379 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable File
		
	
	
	
	
| #! /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),
 | |
|               "ONF": (0x4f4e4600, 10),
 | |
|               "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,
 | |
|               enum ofp_version version, 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, version, 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 *, enum ofp_version, " % base_argtype
 | |
|                 else:
 | |
|                     prototype += "%s, enum ofp_version, " % base_argtype
 | |
|             prototype += "struct ofpbuf *);"
 | |
|             print prototype
 | |
| 
 | |
|         print """
 | |
| static enum ofperr ofpact_decode(const struct ofp_action_header *,
 | |
|                                  enum ofp_raw_action_type raw,
 | |
|                                  enum ofp_version version,
 | |
|                                  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)
 | |
| 
 |