9 # Maps from user-friendly version number to its protocol encoding.
10 VERSION = {"1.0": 0x01,
17 NX_VENDOR_ID = 0x00002320
18 ONF_VENDOR_ID = 0x4f4e4600
21 OFPT10_STATS_REQUEST = 16
22 OFPT10_STATS_REPLY = 17
23 OFPT11_STATS_REQUEST = 18
24 OFPT11_STATS_REPLY = 19
27 def decode_version_range(range):
29 return (VERSION[range], VERSION[range])
30 elif range.endswith('+'):
31 return (VERSION[range[:-1]], max(VERSION.values()))
32 elif range == '<all>':
35 a, b = re.match(r'^([^-]+)-([^-]+)$', range).groups()
36 return (VERSION[a], VERSION[b])
41 line = input_file.readline()
44 fatal("unexpected end of input")
49 sys.stderr.write("%s:%d: %s\n" % (file_name, line_number, msg))
57 argv0 = os.path.basename(sys.argv[0])
59 %(argv0)s, for extracting OpenFlow message types from header files
60 usage: %(argv0)s INPUT OUTPUT
61 where INPUT is the name of the input header file
62 and OUTPUT is the output file name.
63 Despite OUTPUT, the output is written to stdout, and the OUTPUT argument
64 only controls #line directives in the output.\
65 ''' % {"argv0": argv0}
69 m = re.match(r'(.*) up to (.*)', s)
71 struct, member = m.groups()
72 return "offsetof(%s, %s)" % (struct, member)
74 return "sizeof(%s)" % s
76 def extract_ofp_msgs(output_file_name):
85 if re.match('enum ofpraw', line):
90 first_line_number = line_number
91 here = '%s:%d' % (file_name, line_number)
92 if (line.startswith('/*')
93 or line.startswith(' *')
97 elif re.match('}', line):
100 if not line.lstrip().startswith('/*'):
101 fatal("unexpected syntax between ofpraw types")
103 comment = line.lstrip()[2:].strip()
104 while not comment.endswith('*/'):
106 if line.startswith('/*') or not line or line.isspace():
107 fatal("unexpected syntax within message")
108 comment += ' %s' % line.lstrip('* \t').rstrip(' \t\r\n')
109 comment = comment[:-2].rstrip()
111 m = re.match(r'([A-Z]+) ([-.+\d]+|<all>) \((\d+)\): ([^.]+)\.$', comment)
113 fatal("unexpected syntax between messages")
114 type_, versions, number, contents = m.groups()
118 m = re.match('\s+(?:OFPRAW_%s)(\d*)_([A-Z0-9_]+),?$' % type_,
121 fatal("syntax error expecting OFPRAW_ enum")
122 vinfix, name = m.groups()
123 rawname = 'OFPRAW_%s%s_%s' % (type_, vinfix, name)
125 min_version, max_version = decode_version_range(versions)
127 human_name = '%s_%s' % (type_, name)
128 if type_.endswith('ST'):
129 if rawname.endswith('_REQUEST'):
130 human_name = human_name[:-8] + " request"
131 elif rawname.endswith('_REPLY'):
132 human_name = human_name[:-6] + " reply"
134 fatal("%s messages are statistics but %s doesn't end "
135 "in _REQUEST or _REPLY" % (type_, rawname))
138 for version in range(min_version, max_version + 1):
140 if number == OFPT_VENDOR:
141 fatal("OFPT (%d) is used for vendor extensions"
143 elif (version == VERSION["1.0"]
144 and (number == OFPT10_STATS_REQUEST
145 or number == OFPT10_STATS_REPLY)):
146 fatal("OFPT 1.0 (%d) is used for stats messages"
148 elif (version != VERSION["1.0"]
149 and (number == OFPT11_STATS_REQUEST
150 or number == OFPT11_STATS_REPLY)):
151 fatal("OFPT 1.1+ (%d) is used for stats messages"
153 hdrs = (version, number, 0, 0, 0)
154 elif type_ == 'OFPST' and name.endswith('_REQUEST'):
155 if version == VERSION["1.0"]:
156 hdrs = (version, OFPT10_STATS_REQUEST, number, 0, 0)
158 hdrs = (version, OFPT11_STATS_REQUEST, number, 0, 0)
159 elif type_ == 'OFPST' and name.endswith('_REPLY'):
160 if version == VERSION["1.0"]:
161 hdrs = (version, OFPT10_STATS_REPLY, number, 0, 0)
163 hdrs = (version, OFPT11_STATS_REPLY, number, 0, 0)
165 hdrs = (version, OFPT_VENDOR, 0, ONF_VENDOR_ID, number)
166 elif type_ == 'ONFST' and name.endswith('_REQUEST'):
167 if version == VERSION["1.0"]:
168 hdrs = (version, OFPT10_STATS_REQUEST, OFPST_VENDOR,
169 ONF_VENDOR_ID, number)
171 hdrs = (version, OFPT11_STATS_REQUEST, OFPST_VENDOR,
172 ONF_VENDOR_ID, number)
173 elif type_ == 'ONFST' and name.endswith('_REPLY'):
174 if version == VERSION["1.0"]:
175 hdrs = (version, OFPT10_STATS_REPLY, OFPST_VENDOR,
176 ONF_VENDOR_ID, number)
178 hdrs = (version, OFPT11_STATS_REPLY, OFPST_VENDOR,
179 ONF_VENDOR_ID, number)
181 hdrs = (version, OFPT_VENDOR, 0, NX_VENDOR_ID, number)
182 elif type_ == 'NXST' and name.endswith('_REQUEST'):
183 if version == VERSION["1.0"]:
184 hdrs = (version, OFPT10_STATS_REQUEST, OFPST_VENDOR,
185 NX_VENDOR_ID, number)
187 hdrs = (version, OFPT11_STATS_REQUEST, OFPST_VENDOR,
188 NX_VENDOR_ID, number)
189 elif type_ == 'NXST' and name.endswith('_REPLY'):
190 if version == VERSION["1.0"]:
191 hdrs = (version, OFPT10_STATS_REPLY, OFPST_VENDOR,
192 NX_VENDOR_ID, number)
194 hdrs = (version, OFPT11_STATS_REPLY, OFPST_VENDOR,
195 NX_VENDOR_ID, number)
197 fatal("type '%s' unknown" % type_)
200 error("Duplicate message definition for %s." % str(hdrs))
201 sys.stderr.write("%s: Here is the location "
202 "of the previous definition.\n"
204 all_hdrs[hdrs] = here
205 these_hdrs.append(hdrs)
208 if contents == 'void':
212 for c in [s.strip() for s in contents.split(",")]:
214 if extra_multiple == '0':
215 extra_multiple = make_sizeof(c[:-2])
217 error("Cannot have multiple [] elements")
219 min_body_elem.append(c)
222 min_body = " + ".join([make_sizeof(s)
223 for s in min_body_elem])
225 if extra_multiple == '0':
226 error("Must specify contents (use 'void' if empty)")
229 if rawname in all_raws:
230 fatal("%s: Duplicate name" % rawname)
232 all_raws[rawname] = {"hdrs": these_hdrs,
233 "min_version": min_version,
234 "max_version": max_version,
235 "min_body": min_body,
236 "extra_multiple": extra_multiple,
238 "human_name": human_name,
239 "line": first_line_number}
240 all_raws_order.append(rawname)
246 if re.match('enum ofptype', line):
252 if re.match(r'\s*/?\*', line) or line.isspace():
254 elif re.match('}', line):
257 if not re.match(r'\s*OFPTYPE_.*/\*', line):
258 fatal("unexpected syntax between OFPTYPE_ definitions")
260 syntax = line.strip()
261 while not syntax.endswith('*/'):
263 if not line.strip().startswith('*'):
264 fatal("unexpected syntax within OFPTYPE_ definition")
265 syntax += ' %s' % line.strip().lstrip('* \t')
266 syntax = syntax.strip()
268 m = re.match(r'(OFPTYPE_[A-Z0-9_]+),\s*/\* (.*) \*/', syntax)
270 fatal("syntax error in OFPTYPE_ definition")
272 ofptype, raws_ = m.groups()
273 raws = [s.rstrip('.') for s in raws_.split()]
275 if not re.match('OFPRAW_[A-Z0-9_]+$', raw):
276 fatal("%s: invalid OFPRAW_* name syntax" % raw)
277 if raw not in all_raws:
278 fatal("%s: not a declared OFPRAW_* name" % raw)
279 if "ofptype" in all_raws[raw]:
280 fatal("%s: already part of %s"
281 % (raw, all_raws[raw]["ofptype"]))
282 all_raws[raw]["ofptype"] = ofptype
284 all_types.append(all_raws[raws[0]]["human_name"])
292 output.append("/* Generated automatically; do not modify! "
293 "-*- buffer-read-only: t -*- */")
296 for raw in all_raws_order:
298 output.append("static struct raw_instance %s_instances[] = {"
300 for hdrs in r['hdrs']:
301 output.append(" { {0, NULL}, {%d, %d, %d, 0x%x, %d}, %s, 0 },"
308 output.append("static struct raw_info raw_infos[] = {")
309 for raw in all_raws_order:
311 if "ofptype" not in r:
312 error("%s: no defined OFPTYPE_" % raw)
315 output.append(" %s_instances," % raw.lower())
316 output.append(" %d, %d," % (r["min_version"], r["max_version"]))
317 output.append("#line %s \"%s\"" % (r["line"], file_name))
318 output.append(" %s," % r["min_body"])
319 output.append("#line %s \"%s\"" % (r["line"], file_name))
320 output.append(" %s," % r["extra_multiple"])
321 output.append("#line %s \"%s\"" % (len(output) + 2, output_file_name))
322 output.append(" %s," % r["ofptype"])
323 output.append(" \"%s\"," % r["human_name"])
326 if r['type'].endswith("ST"):
327 for hdrs in r['hdrs']:
329 if hdrs[0] == VERSION["1.0"]:
330 if hdrs[1] == OFPT10_STATS_REQUEST:
331 op_hdrs[1] = OFPT10_STATS_REPLY
332 elif hdrs[1] == OFPT10_STATS_REPLY:
333 op_hdrs[1] = OFPT10_STATS_REQUEST
337 if hdrs[1] == OFPT11_STATS_REQUEST:
338 op_hdrs[1] = OFPT11_STATS_REPLY
339 elif hdrs[1] == OFPT11_STATS_REPLY:
340 op_hdrs[1] = OFPT11_STATS_REQUEST
343 if tuple(op_hdrs) not in all_hdrs:
344 if r["human_name"].endswith("request"):
345 fatal("%s has no corresponding reply"
348 fatal("%s has no corresponding request"
353 output.append("static const char *type_names[] = {");
355 output.append(" \"%s\"," % t)
364 if __name__ == '__main__':
365 if '--help' in sys.argv:
367 elif len(sys.argv) != 3:
368 sys.stderr.write("exactly two non-option arguments required; "
369 "use --help for help\n")
375 file_name = sys.argv[1]
376 input_file = open(file_name)
379 for line in extract_ofp_msgs(sys.argv[2]):