382 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			382 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable File
		
	
	
	
	
| #! /usr/bin/python
 | |
| 
 | |
| import sys
 | |
| import os.path
 | |
| import re
 | |
| 
 | |
| line = ""
 | |
| 
 | |
| # Maps from user-friendly version number to its protocol encoding.
 | |
| VERSION = {"1.0": 0x01,
 | |
|            "1.1": 0x02,
 | |
|            "1.2": 0x03,
 | |
|            "1.3": 0x04,
 | |
|            "1.4": 0x05,
 | |
|            "1.5": 0x06}
 | |
| 
 | |
| NX_VENDOR_ID = 0x00002320
 | |
| ONF_VENDOR_ID = 0x4f4e4600
 | |
| 
 | |
| OFPT_VENDOR = 4
 | |
| OFPT10_STATS_REQUEST = 16
 | |
| OFPT10_STATS_REPLY = 17
 | |
| OFPT11_STATS_REQUEST = 18
 | |
| OFPT11_STATS_REPLY = 19
 | |
| OFPST_VENDOR = 0xffff
 | |
| 
 | |
| def decode_version_range(range):
 | |
|     if range in VERSION:
 | |
|         return (VERSION[range], VERSION[range])
 | |
|     elif range.endswith('+'):
 | |
|         return (VERSION[range[:-1]], max(VERSION.values()))
 | |
|     elif range == '<all>':
 | |
|         return (0x01, 0xff)
 | |
|     else:
 | |
|         a, b = re.match(r'^([^-]+)-([^-]+)$', range).groups()
 | |
|         return (VERSION[a], VERSION[b])
 | |
| 
 | |
| def get_line():
 | |
|     global line
 | |
|     global line_number
 | |
|     line = input_file.readline()
 | |
|     line_number += 1
 | |
|     if line == "":
 | |
|         fatal("unexpected end of input")
 | |
| 
 | |
| 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 message types from header files
 | |
| usage: %(argv0)s INPUT OUTPUT
 | |
|   where INPUT is the name of the input header file
 | |
|     and OUTPUT is the output file name.
 | |
| Despite OUTPUT, the output is written to stdout, and the OUTPUT argument
 | |
| only controls #line directives in the output.\
 | |
| ''' % {"argv0": argv0}
 | |
|     sys.exit(0)
 | |
| 
 | |
| def make_sizeof(s):
 | |
|     m = re.match(r'(.*) up to (.*)', s)
 | |
|     if m:
 | |
|         struct, member = m.groups()
 | |
|         return "offsetof(%s, %s)" % (struct, member)
 | |
|     else:
 | |
|         return "sizeof(%s)" % s
 | |
| 
 | |
| def extract_ofp_msgs(output_file_name):
 | |
|     raw_types = []
 | |
| 
 | |
|     all_hdrs = {}
 | |
|     all_raws = {}
 | |
|     all_raws_order = []
 | |
| 
 | |
|     while True:
 | |
|         get_line()
 | |
|         if re.match('enum ofpraw', line):
 | |
|             break
 | |
| 
 | |
|     while True:
 | |
|         get_line()
 | |
|         first_line_number = line_number
 | |
|         here = '%s:%d' % (file_name, line_number)
 | |
|         if (line.startswith('/*')
 | |
|             or line.startswith(' *')
 | |
|             or not line
 | |
|             or line.isspace()):
 | |
|             continue
 | |
|         elif re.match('}', line):
 | |
|             break
 | |
| 
 | |
|         if not line.lstrip().startswith('/*'):
 | |
|             fatal("unexpected syntax between ofpraw types")
 | |
| 
 | |
|         comment = line.lstrip()[2:].strip()
 | |
|         while not comment.endswith('*/'):
 | |
|             get_line()
 | |
|             if line.startswith('/*') or not line or line.isspace():
 | |
|                 fatal("unexpected syntax within message")
 | |
|             comment += ' %s' % line.lstrip('* \t').rstrip(' \t\r\n')
 | |
|         comment = comment[:-2].rstrip()
 | |
| 
 | |
|         m = re.match(r'([A-Z]+) ([-.+\d]+|<all>) \((\d+)\): ([^.]+)\.$', comment)
 | |
|         if not m:
 | |
|             fatal("unexpected syntax between messages")
 | |
|         type_, versions, number, contents = m.groups()
 | |
|         number = int(number)
 | |
| 
 | |
|         get_line()
 | |
|         m = re.match('\s+(?:OFPRAW_%s)(\d*)_([A-Z0-9_]+),?$' % type_,
 | |
|                      line)
 | |
|         if not m:
 | |
|             fatal("syntax error expecting OFPRAW_ enum")
 | |
|         vinfix, name = m.groups()
 | |
|         rawname = 'OFPRAW_%s%s_%s' % (type_, vinfix, name)
 | |
| 
 | |
|         min_version, max_version = decode_version_range(versions)
 | |
| 
 | |
|         human_name = '%s_%s' % (type_, name)
 | |
|         if type_.endswith('ST'):
 | |
|             if rawname.endswith('_REQUEST'):
 | |
|                 human_name = human_name[:-8] + " request"
 | |
|             elif rawname.endswith('_REPLY'):
 | |
|                 human_name = human_name[:-6] + " reply"
 | |
|             else:
 | |
|                 fatal("%s messages are statistics but %s doesn't end "
 | |
|                       "in _REQUEST or _REPLY" % (type_, rawname))
 | |
| 
 | |
|         these_hdrs = []
 | |
|         for version in range(min_version, max_version + 1):
 | |
|             if type_ == 'OFPT':
 | |
|                 if number == OFPT_VENDOR:
 | |
|                     fatal("OFPT (%d) is used for vendor extensions"
 | |
|                           % number)
 | |
|                 elif (version == VERSION["1.0"]
 | |
|                       and (number == OFPT10_STATS_REQUEST
 | |
|                            or number == OFPT10_STATS_REPLY)):
 | |
|                     fatal("OFPT 1.0 (%d) is used for stats messages"
 | |
|                           % number)
 | |
|                 elif (version != VERSION["1.0"]
 | |
|                       and (number == OFPT11_STATS_REQUEST
 | |
|                            or number == OFPT11_STATS_REPLY)):
 | |
|                     fatal("OFPT 1.1+ (%d) is used for stats messages"
 | |
|                           % number)
 | |
|                 hdrs = (version, number, 0, 0, 0)
 | |
|             elif type_ == 'OFPST' and name.endswith('_REQUEST'):
 | |
|                 if version == VERSION["1.0"]:
 | |
|                     hdrs = (version, OFPT10_STATS_REQUEST, number, 0, 0)
 | |
|                 else:
 | |
|                     hdrs = (version, OFPT11_STATS_REQUEST, number, 0, 0)
 | |
|             elif type_ == 'OFPST' and name.endswith('_REPLY'):
 | |
|                 if version == VERSION["1.0"]:
 | |
|                     hdrs = (version, OFPT10_STATS_REPLY, number, 0, 0)
 | |
|                 else:
 | |
|                     hdrs = (version, OFPT11_STATS_REPLY, number, 0, 0)
 | |
|             elif type_ == 'ONF':
 | |
|                 hdrs = (version, OFPT_VENDOR, 0, ONF_VENDOR_ID, number)
 | |
|             elif type_ == 'ONFST' and name.endswith('_REQUEST'):
 | |
|                 if version == VERSION["1.0"]:
 | |
|                     hdrs = (version, OFPT10_STATS_REQUEST, OFPST_VENDOR,
 | |
|                             ONF_VENDOR_ID, number)
 | |
|                 else:
 | |
|                     hdrs = (version, OFPT11_STATS_REQUEST, OFPST_VENDOR,
 | |
|                             ONF_VENDOR_ID, number)
 | |
|             elif type_ == 'ONFST' and name.endswith('_REPLY'):
 | |
|                 if version == VERSION["1.0"]:
 | |
|                     hdrs = (version, OFPT10_STATS_REPLY, OFPST_VENDOR,
 | |
|                             ONF_VENDOR_ID, number)
 | |
|                 else:
 | |
|                     hdrs = (version, OFPT11_STATS_REPLY, OFPST_VENDOR,
 | |
|                             ONF_VENDOR_ID, number)
 | |
|             elif type_ == 'NXT':
 | |
|                 hdrs = (version, OFPT_VENDOR, 0, NX_VENDOR_ID, number)
 | |
|             elif type_ == 'NXST' and name.endswith('_REQUEST'):
 | |
|                 if version == VERSION["1.0"]:
 | |
|                     hdrs = (version, OFPT10_STATS_REQUEST, OFPST_VENDOR,
 | |
|                             NX_VENDOR_ID, number)
 | |
|                 else:
 | |
|                     hdrs = (version, OFPT11_STATS_REQUEST, OFPST_VENDOR,
 | |
|                             NX_VENDOR_ID, number)
 | |
|             elif type_ == 'NXST' and name.endswith('_REPLY'):
 | |
|                 if version == VERSION["1.0"]:
 | |
|                     hdrs = (version, OFPT10_STATS_REPLY, OFPST_VENDOR,
 | |
|                             NX_VENDOR_ID, number)
 | |
|                 else:
 | |
|                     hdrs = (version, OFPT11_STATS_REPLY, OFPST_VENDOR,
 | |
|                             NX_VENDOR_ID, number)
 | |
|             else:
 | |
|                 fatal("type '%s' unknown" % type_)
 | |
| 
 | |
|             if hdrs in all_hdrs:
 | |
|                 error("Duplicate message definition for %s." % str(hdrs))
 | |
|                 sys.stderr.write("%s: Here is the location "
 | |
|                                  "of the previous definition.\n"
 | |
|                                  % (all_hdrs[hdrs]))
 | |
|             all_hdrs[hdrs] = here
 | |
|             these_hdrs.append(hdrs)
 | |
| 
 | |
|         extra_multiple = '0'
 | |
|         if contents == 'void':
 | |
|             min_body = '0'
 | |
|         else:
 | |
|             min_body_elem = []
 | |
|             for c in [s.strip() for s in contents.split(",")]:
 | |
|                 if c.endswith('[]'):
 | |
|                     if extra_multiple == '0':
 | |
|                         extra_multiple = make_sizeof(c[:-2])
 | |
|                     else:
 | |
|                         error("Cannot have multiple [] elements")
 | |
|                 else:
 | |
|                     min_body_elem.append(c)
 | |
| 
 | |
|             if min_body_elem:
 | |
|                 min_body = " + ".join([make_sizeof(s)
 | |
|                                        for s in min_body_elem])
 | |
|             else:
 | |
|                 if extra_multiple == '0':
 | |
|                     error("Must specify contents (use 'void' if empty)")
 | |
|                 min_body = 0
 | |
| 
 | |
|         if rawname in all_raws:
 | |
|             fatal("%s: Duplicate name" % rawname)
 | |
| 
 | |
|         all_raws[rawname] = {"hdrs": these_hdrs,
 | |
|                              "min_version": min_version,
 | |
|                              "max_version": max_version,
 | |
|                              "min_body": min_body,
 | |
|                              "extra_multiple": extra_multiple,
 | |
|                              "type": type_,
 | |
|                              "human_name": human_name,
 | |
|                              "line": first_line_number}
 | |
|         all_raws_order.append(rawname)
 | |
| 
 | |
|         continue
 | |
| 
 | |
|     while True:
 | |
|         get_line()
 | |
|         if re.match('enum ofptype', line):
 | |
|             break
 | |
| 
 | |
|     all_types = []
 | |
|     while True:
 | |
|         get_line()
 | |
|         if re.match(r'\s*/?\*', line) or line.isspace():
 | |
|             continue
 | |
|         elif re.match('}', line):
 | |
|             break
 | |
| 
 | |
|         if not re.match(r'\s*OFPTYPE_.*/\*', line):
 | |
|             fatal("unexpected syntax between OFPTYPE_ definitions")
 | |
| 
 | |
|         syntax = line.strip()
 | |
|         while not syntax.endswith('*/'):
 | |
|             get_line()
 | |
|             if not line.strip().startswith('*'):
 | |
|                 fatal("unexpected syntax within OFPTYPE_ definition")
 | |
|             syntax += ' %s' % line.strip().lstrip('* \t')
 | |
|             syntax = syntax.strip()
 | |
| 
 | |
|         m = re.match(r'(OFPTYPE_[A-Z0-9_]+),\s*/\* (.*) \*/', syntax)
 | |
|         if not m:
 | |
|             fatal("syntax error in OFPTYPE_ definition")
 | |
| 
 | |
|         ofptype, raws_ = m.groups()
 | |
|         raws = [s.rstrip('.') for s in raws_.split()]
 | |
|         for raw in raws:
 | |
|             if not re.match('OFPRAW_[A-Z0-9_]+$', raw):
 | |
|                 fatal("%s: invalid OFPRAW_* name syntax" % raw)
 | |
|             if raw not in all_raws:
 | |
|                 fatal("%s: not a declared OFPRAW_* name" % raw)
 | |
|             if "ofptype" in all_raws[raw]:
 | |
|                 fatal("%s: already part of %s"
 | |
|                       % (raw, all_raws[raw]["ofptype"]))
 | |
|             all_raws[raw]["ofptype"] = ofptype
 | |
| 
 | |
|         all_types.append(all_raws[raws[0]]["human_name"])
 | |
| 
 | |
|     input_file.close()
 | |
| 
 | |
|     if n_errors:
 | |
|         sys.exit(1)
 | |
| 
 | |
|     output = []
 | |
|     output.append("/* Generated automatically; do not modify!     "
 | |
|                   "-*- buffer-read-only: t -*- */")
 | |
|     output.append("")
 | |
| 
 | |
|     for raw in all_raws_order:
 | |
|         r = all_raws[raw]
 | |
|         output.append("static struct raw_instance %s_instances[] = {"
 | |
|                       % raw.lower())
 | |
|         for hdrs in r['hdrs']:
 | |
|             output.append("    { {0, NULL}, {%d, %d, %d, 0x%x, %d}, %s, 0 },"
 | |
|                           % (hdrs + (raw,)))
 | |
|                 
 | |
|         output.append("};")
 | |
| 
 | |
|     output.append("")
 | |
| 
 | |
|     output.append("static struct raw_info raw_infos[] = {")
 | |
|     for raw in all_raws_order:
 | |
|         r = all_raws[raw]
 | |
|         if "ofptype" not in r:
 | |
|             error("%s: no defined OFPTYPE_" % raw)
 | |
|             continue
 | |
|         output.append("    {")
 | |
|         output.append("        %s_instances," % raw.lower())
 | |
|         output.append("        %d, %d," % (r["min_version"], r["max_version"]))
 | |
|         output.append("#line %s \"%s\"" % (r["line"], file_name))
 | |
|         output.append("        %s," % r["min_body"])
 | |
|         output.append("#line %s \"%s\"" % (r["line"], file_name))
 | |
|         output.append("        %s," % r["extra_multiple"])
 | |
|         output.append("#line %s \"%s\"" % (len(output) + 2, output_file_name))
 | |
|         output.append("        %s," % r["ofptype"])
 | |
|         output.append("        \"%s\"," % r["human_name"])
 | |
|         output.append("    },")
 | |
| 
 | |
|         if r['type'].endswith("ST"):
 | |
|             for hdrs in r['hdrs']:
 | |
|                 op_hdrs = list(hdrs)
 | |
|                 if hdrs[0] == VERSION["1.0"]:
 | |
|                     if hdrs[1] == OFPT10_STATS_REQUEST:
 | |
|                         op_hdrs[1] = OFPT10_STATS_REPLY
 | |
|                     elif hdrs[1] == OFPT10_STATS_REPLY:
 | |
|                         op_hdrs[1] = OFPT10_STATS_REQUEST
 | |
|                     else:
 | |
|                         assert False
 | |
|                 else:
 | |
|                     if hdrs[1] == OFPT11_STATS_REQUEST:
 | |
|                         op_hdrs[1] = OFPT11_STATS_REPLY
 | |
|                     elif hdrs[1] == OFPT11_STATS_REPLY:
 | |
|                         op_hdrs[1] = OFPT11_STATS_REQUEST
 | |
|                     else:
 | |
|                         assert False
 | |
|                 if tuple(op_hdrs) not in all_hdrs:
 | |
|                     if r["human_name"].endswith("request"):
 | |
|                         fatal("%s has no corresponding reply"
 | |
|                               % r["human_name"])
 | |
|                     else:
 | |
|                         fatal("%s has no corresponding request"
 | |
|                               % r["human_name"])
 | |
|     output.append("};")
 | |
| 
 | |
|     output.append("");
 | |
|     output.append("static const char *type_names[] = {");
 | |
|     for t in all_types:
 | |
|         output.append("    \"%s\"," % t)
 | |
|     output.append("};")
 | |
| 
 | |
|     if n_errors:
 | |
|         sys.exit(1)
 | |
| 
 | |
|     return output
 | |
| 
 | |
| 
 | |
| if __name__ == '__main__':
 | |
|     if '--help' in sys.argv:
 | |
|         usage()
 | |
|     elif len(sys.argv) != 3:
 | |
|         sys.stderr.write("exactly two non-option arguments required; "
 | |
|                          "use --help for help\n")
 | |
|         sys.exit(1)
 | |
|     else:
 | |
|         global file_name
 | |
|         global input_file
 | |
|         global line_number
 | |
|         file_name = sys.argv[1]
 | |
|         input_file = open(file_name)
 | |
|         line_number = 0
 | |
| 
 | |
|         for line in extract_ofp_msgs(sys.argv[2]):
 | |
|             print line
 | |
|         
 |