9 # Maps from user-friendly version number to its protocol encoding.
10 VERSION = {"1.0": 0x01,
17 NX_VENDOR_ID = 0x00002320
20 OFPT10_STATS_REQUEST = 16
21 OFPT10_STATS_REPLY = 17
22 OFPT11_STATS_REQUEST = 18
23 OFPT11_STATS_REPLY = 19
26 def decode_version_range(range):
28 return (VERSION[range], VERSION[range])
29 elif range.endswith('+'):
30 return (VERSION[range[:-1]], max(VERSION.values()))
31 elif range == '<all>':
34 a, b = re.match(r'^([^-]+)-([^-]+)$', range).groups()
35 return (VERSION[a], VERSION[b])
40 line = input_file.readline()
43 fatal("unexpected end of input")
48 sys.stderr.write("%s:%d: %s\n" % (file_name, line_number, msg))
56 argv0 = os.path.basename(sys.argv[0])
58 %(argv0)s, for extracting OpenFlow message types from header files
59 usage: %(argv0)s INPUT OUTPUT
60 where INPUT is the name of the input header file
61 and OUTPUT is the output file name.
62 Despite OUTPUT, the output is written to stdout, and the OUTPUT argument
63 only controls #line directives in the output.\
64 ''' % {"argv0": argv0}
68 m = re.match(r'(.*) up to (.*)', s)
70 struct, member = m.groups()
71 return "offsetof(%s, %s)" % (struct, member)
73 return "sizeof(%s)" % s
75 def extract_ofp_msgs(output_file_name):
84 if re.match('enum ofpraw', line):
89 first_line_number = line_number
90 here = '%s:%d' % (file_name, line_number)
91 if (line.startswith('/*')
92 or line.startswith(' *')
96 elif re.match('}', line):
99 if not line.lstrip().startswith('/*'):
100 fatal("unexpected syntax between ofpraw types")
102 comment = line.lstrip()[2:].strip()
103 while not comment.endswith('*/'):
105 if line.startswith('/*') or not line or line.isspace():
106 fatal("unexpected syntax within error")
107 comment += ' %s' % line.lstrip('* \t').rstrip(' \t\r\n')
108 comment = comment[:-2].rstrip()
110 m = re.match(r'([A-Z]+) ([-.+\d]+|<all>) \((\d+)\): ([^.]+)\.$', comment)
112 fatal("unexpected syntax between messages")
113 type_, versions, number, contents = m.groups()
117 m = re.match('\s+(?:OFPRAW_%s)(\d*)_([A-Z0-9_]+),?$' % type_,
120 fatal("syntax error expecting OFPRAW_ enum")
121 vinfix, name = m.groups()
122 rawname = 'OFPRAW_%s%s_%s' % (type_, vinfix, name)
124 min_version, max_version = decode_version_range(versions)
126 human_name = '%s_%s' % (type_, name)
127 if type_.endswith('ST'):
128 if rawname.endswith('_REQUEST'):
129 human_name = human_name[:-8] + " request"
130 elif rawname.endswith('_REPLY'):
131 human_name = human_name[:-6] + " reply"
133 fatal("%s messages are statistics but %s doesn't end "
134 "in _REQUEST or _REPLY" % (type_, rawname))
137 for version in range(min_version, max_version + 1):
139 if number == OFPT_VENDOR:
140 fatal("OFPT (%d) is used for vendor extensions"
142 elif (version == VERSION["1.0"]
143 and (number == OFPT10_STATS_REQUEST
144 or number == OFPT10_STATS_REPLY)):
145 fatal("OFPT 1.0 (%d) is used for stats messages"
147 elif (version != VERSION["1.0"]
148 and (number == OFPT11_STATS_REQUEST
149 or number == OFPT11_STATS_REPLY)):
150 fatal("OFPT 1.1+ (%d) is used for stats messages"
152 hdrs = (version, number, 0, 0, 0)
153 elif type_ == 'OFPST' and name.endswith('_REQUEST'):
154 if version == VERSION["1.0"]:
155 hdrs = (version, OFPT10_STATS_REQUEST, number, 0, 0)
157 hdrs = (version, OFPT11_STATS_REQUEST, number, 0, 0)
158 elif type_ == 'OFPST' and name.endswith('_REPLY'):
159 if version == VERSION["1.0"]:
160 hdrs = (version, OFPT10_STATS_REPLY, number, 0, 0)
162 hdrs = (version, OFPT11_STATS_REPLY, number, 0, 0)
164 hdrs = (version, OFPT_VENDOR, 0, NX_VENDOR_ID, number)
165 elif type_ == 'NXST' and name.endswith('_REQUEST'):
166 if version == VERSION["1.0"]:
167 hdrs = (version, OFPT10_STATS_REQUEST, OFPST_VENDOR,
168 NX_VENDOR_ID, number)
170 hdrs = (version, OFPT11_STATS_REQUEST, OFPST_VENDOR,
171 NX_VENDOR_ID, number)
172 elif type_ == 'NXST' and name.endswith('_REPLY'):
173 if version == VERSION["1.0"]:
174 hdrs = (version, OFPT10_STATS_REPLY, OFPST_VENDOR,
175 NX_VENDOR_ID, number)
177 hdrs = (version, OFPT11_STATS_REPLY, OFPST_VENDOR,
178 NX_VENDOR_ID, number)
180 fatal("type '%s' unknown" % type_)
183 error("Duplicate message definition for %s." % str(hdrs))
184 sys.stderr.write("%s: Here is the location "
185 "of the previous definition.\n"
187 all_hdrs[hdrs] = here
188 these_hdrs.append(hdrs)
191 if contents == 'void':
195 for c in [s.strip() for s in contents.split(",")]:
197 if extra_multiple == '0':
198 extra_multiple = make_sizeof(c[:-2])
200 error("Cannot have multiple [] elements")
202 min_body_elem.append(c)
205 min_body = " + ".join([make_sizeof(s)
206 for s in min_body_elem])
208 if extra_multiple == '0':
209 error("Must specify contents (use 'void' if empty)")
212 if rawname in all_raws:
213 fatal("%s: Duplicate name" % rawname)
215 all_raws[rawname] = {"hdrs": these_hdrs,
216 "min_version": min_version,
217 "max_version": max_version,
218 "min_body": min_body,
219 "extra_multiple": extra_multiple,
221 "human_name": human_name,
222 "line": first_line_number}
223 all_raws_order.append(rawname)
229 if re.match('enum ofptype', line):
234 if re.match(r'\s*/?\*', line) or line.isspace():
236 elif re.match('}', line):
239 if not re.match(r'\s*OFPTYPE_.*/\*', line):
240 fatal("unexpected syntax between OFPTYPE_ definitions")
242 syntax = line.strip()
243 while not syntax.endswith('*/'):
245 if not line.strip().startswith('*'):
246 fatal("unexpected syntax within OFPTYPE_ definition")
247 syntax += ' %s' % line.strip().lstrip('* \t')
248 syntax = syntax.strip()
250 m = re.match(r'(OFPTYPE_[A-Z0-9_]+),\s*/\* (.*) \*/', syntax)
252 fatal("syntax error in OFPTYPE_ definition")
254 ofptype, raws_ = m.groups()
255 raws = [s.rstrip('.') for s in raws_.split()]
257 if not re.match('OFPRAW_[A-Z0-9_]+$', raw):
258 fatal("%s: invalid OFPRAW_* name syntax" % raw)
259 if raw not in all_raws:
260 fatal("%s: not a declared OFPRAW_* name" % raw)
261 if "ofptype" in all_raws[raw]:
262 fatal("%s: already part of %s"
263 % (raw, all_raws[raw]["ofptype"]))
264 all_raws[raw]["ofptype"] = ofptype
272 output.append("/* Generated automatically; do not modify! "
273 "-*- buffer-read-only: t -*- */")
276 for raw in all_raws_order:
278 output.append("static struct raw_instance %s_instances[] = {"
280 for hdrs in r['hdrs']:
281 output.append(" { {0, NULL}, {%d, %d, %d, 0x%x, %d}, %s, 0 },"
288 output.append("static struct raw_info raw_infos[] = {")
289 for raw in all_raws_order:
291 if "ofptype" not in r:
292 error("%s: no defined OFPTYPE_" % raw)
295 output.append(" %s_instances," % raw.lower())
296 output.append(" %d, %d," % (r["min_version"], r["max_version"]))
297 output.append("#line %s \"%s\"" % (r["line"], file_name))
298 output.append(" %s," % r["min_body"])
299 output.append("#line %s \"%s\"" % (r["line"], file_name))
300 output.append(" %s," % r["extra_multiple"])
301 output.append("#line %s \"%s\"" % (len(output) + 2, output_file_name))
302 output.append(" %s," % r["ofptype"])
303 output.append(" \"%s\"," % r["human_name"])
306 if r['type'].endswith("ST"):
307 for hdrs in r['hdrs']:
309 if hdrs[0] == VERSION["1.0"]:
310 if hdrs[1] == OFPT10_STATS_REQUEST:
311 op_hdrs[1] = OFPT10_STATS_REPLY
312 elif hdrs[1] == OFPT10_STATS_REPLY:
313 op_hdrs[1] = OFPT10_STATS_REQUEST
317 if hdrs[1] == OFPT11_STATS_REQUEST:
318 op_hdrs[1] = OFPT11_STATS_REPLY
319 elif hdrs[1] == OFPT11_STATS_REPLY:
320 op_hdrs[1] = OFPT11_STATS_REQUEST
323 if tuple(op_hdrs) not in all_hdrs:
324 if r["human_name"].endswith("request"):
325 fatal("%s has no corresponding reply"
328 fatal("%s has no corresponding request"
338 if __name__ == '__main__':
339 if '--help' in sys.argv:
341 elif len(sys.argv) != 3:
342 sys.stderr.write("exactly one non-option arguments required; "
343 "use --help for help\n")
349 file_name = sys.argv[1]
350 input_file = open(file_name)
353 for line in extract_ofp_msgs(sys.argv[2]):