aboutsummaryrefslogtreecommitdiff
path: root/build-aux
diff options
context:
space:
mode:
authorBen Pfaff <blp@nicira.com>2012-07-19 23:23:17 -0700
committerBen Pfaff <blp@nicira.com>2012-07-30 21:09:12 -0700
commit982697a4d24caa0a3bdaf85db67619338b382e50 (patch)
tree028e7fd69f9f836f2785b5096d78eb8046568a80 /build-aux
parent4b3b8d8fa14bbe4000c6591eef74e0a0d51a3929 (diff)
ofp-msgs: New approach to encoding and decoding OpenFlow headers.
OpenFlow headers are not as uniform as they could be, with size, alignment, and numbering changes from one version to another and across varieties (e.g. ordinary messages vs. "stats" messages). Until now the Open vSwitch internal APIs haven't done a good job of abstracting those differences in header formats. This commit changes that; from this commit forward very little code actually needs to understand the header format or numbering. Instead, it can just encode or decode, or pull or put, the header using a more abstract API using the ofpraw_, ofptype_, and other APIs in the new ofp-msgs module. Signed-off-by: Ben Pfaff <blp@nicira.com> Tested-by: Simon Horman <horms@verge.net.au> Reviewed-by: Simon Horman <horms@verge.net.au>
Diffstat (limited to 'build-aux')
-rwxr-xr-xbuild-aux/extract-ofp-msgs351
1 files changed, 351 insertions, 0 deletions
diff --git a/build-aux/extract-ofp-msgs b/build-aux/extract-ofp-msgs
new file mode 100755
index 00000000..fe92dae0
--- /dev/null
+++ b/build-aux/extract-ofp-msgs
@@ -0,0 +1,351 @@
+#! /usr/bin/python
+
+import sys
+import os.path
+import re
+
+line = ""
+
+OFP10_VERSION = 0x01
+OFP11_VERSION = 0x02
+OFP12_VERSION = 0x03
+OFP13_VERSION = 0x04
+
+NX_VENDOR_ID = 0x00002320
+
+OFPT_VENDOR = 4
+OFPT10_STATS_REQUEST = 16
+OFPT10_STATS_REPLY = 17
+OFPT11_STATS_REQUEST = 18
+OFPT11_STATS_REPLY = 19
+OFPST_VENDOR = 0xffff
+
+version_map = {"1.0": (OFP10_VERSION, OFP10_VERSION),
+ "1.1": (OFP11_VERSION, OFP11_VERSION),
+ "1.2": (OFP12_VERSION, OFP12_VERSION),
+ "1.3": (OFP13_VERSION, OFP13_VERSION),
+ "1.0+": (OFP10_VERSION, OFP13_VERSION),
+ "1.1+": (OFP11_VERSION, OFP13_VERSION),
+ "1.2+": (OFP12_VERSION, OFP13_VERSION),
+ "1.3+": (OFP13_VERSION, OFP13_VERSION),
+ "1.0-1.1": (OFP10_VERSION, OFP11_VERSION)}
+
+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 error")
+ comment += ' %s' % line.lstrip('* \t').rstrip(' \t\r\n')
+ comment = comment[:-2].rstrip()
+
+ m = re.match(r'([A-Z]+) ([-.+\d]+) \((\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 = version_map[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 == OFP10_VERSION
+ and (number == OFPT10_STATS_REQUEST
+ or number == OFPT10_STATS_REPLY)):
+ fatal("OFPT 1.0 (%d) is used for stats messages"
+ % number)
+ elif (version != OFP10_VERSION
+ 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 == OFP10_VERSION:
+ 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 == OFP10_VERSION:
+ hdrs = (version, OFPT10_STATS_REPLY, number, 0, 0)
+ else:
+ hdrs = (version, OFPT11_STATS_REPLY, number, 0, 0)
+ elif type_ == 'NXT':
+ hdrs = (version, OFPT_VENDOR, 0, NX_VENDOR_ID, number)
+ elif type_ == 'NXST' and name.endswith('_REQUEST'):
+ if version == OFP10_VERSION:
+ 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 == OFP10_VERSION:
+ 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
+
+ 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
+
+ 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] == OFP10_VERSION:
+ 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("};")
+
+ 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 one 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
+