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)
|
|
|