]>
Commit | Line | Data |
---|---|---|
982697a4 BP |
1 | #! /usr/bin/python |
2 | ||
3 | import sys | |
4 | import os.path | |
5 | import re | |
6 | ||
7 | line = "" | |
8 | ||
42dccab5 BP |
9 | # Maps from user-friendly version number to its protocol encoding. |
10 | VERSION = {"1.0": 0x01, | |
11 | "1.1": 0x02, | |
12 | "1.2": 0x03, | |
13 | "1.3": 0x04, | |
14 | "1.4": 0x05, | |
b79d45a1 BP |
15 | "1.5": 0x06, |
16 | "1.6": 0x07} | |
982697a4 BP |
17 | |
18 | NX_VENDOR_ID = 0x00002320 | |
90219696 | 19 | ONF_VENDOR_ID = 0x4f4e4600 |
982697a4 BP |
20 | |
21 | OFPT_VENDOR = 4 | |
22 | OFPT10_STATS_REQUEST = 16 | |
23 | OFPT10_STATS_REPLY = 17 | |
24 | OFPT11_STATS_REQUEST = 18 | |
25 | OFPT11_STATS_REPLY = 19 | |
26 | OFPST_VENDOR = 0xffff | |
27 | ||
42dccab5 BP |
28 | def decode_version_range(range): |
29 | if range in VERSION: | |
30 | return (VERSION[range], VERSION[range]) | |
31 | elif range.endswith('+'): | |
32 | return (VERSION[range[:-1]], max(VERSION.values())) | |
33 | elif range == '<all>': | |
34 | return (0x01, 0xff) | |
35 | else: | |
36 | a, b = re.match(r'^([^-]+)-([^-]+)$', range).groups() | |
37 | return (VERSION[a], VERSION[b]) | |
982697a4 BP |
38 | |
39 | def get_line(): | |
40 | global line | |
41 | global line_number | |
42 | line = input_file.readline() | |
43 | line_number += 1 | |
44 | if line == "": | |
45 | fatal("unexpected end of input") | |
46 | ||
47 | n_errors = 0 | |
48 | def error(msg): | |
49 | global n_errors | |
50 | sys.stderr.write("%s:%d: %s\n" % (file_name, line_number, msg)) | |
51 | n_errors += 1 | |
52 | ||
53 | def fatal(msg): | |
54 | error(msg) | |
55 | sys.exit(1) | |
56 | ||
57 | def usage(): | |
58 | argv0 = os.path.basename(sys.argv[0]) | |
d34a1cc0 | 59 | print('''\ |
982697a4 BP |
60 | %(argv0)s, for extracting OpenFlow message types from header files |
61 | usage: %(argv0)s INPUT OUTPUT | |
62 | where INPUT is the name of the input header file | |
63 | and OUTPUT is the output file name. | |
64 | Despite OUTPUT, the output is written to stdout, and the OUTPUT argument | |
65 | only controls #line directives in the output.\ | |
d34a1cc0 | 66 | ''' % {"argv0": argv0}) |
982697a4 BP |
67 | sys.exit(0) |
68 | ||
69 | def make_sizeof(s): | |
70 | m = re.match(r'(.*) up to (.*)', s) | |
71 | if m: | |
72 | struct, member = m.groups() | |
73 | return "offsetof(%s, %s)" % (struct, member) | |
74 | else: | |
75 | return "sizeof(%s)" % s | |
76 | ||
77 | def extract_ofp_msgs(output_file_name): | |
78 | raw_types = [] | |
79 | ||
80 | all_hdrs = {} | |
81 | all_raws = {} | |
82 | all_raws_order = [] | |
83 | ||
84 | while True: | |
85 | get_line() | |
86 | if re.match('enum ofpraw', line): | |
87 | break | |
88 | ||
89 | while True: | |
90 | get_line() | |
91 | first_line_number = line_number | |
92 | here = '%s:%d' % (file_name, line_number) | |
93 | if (line.startswith('/*') | |
94 | or line.startswith(' *') | |
95 | or not line | |
96 | or line.isspace()): | |
97 | continue | |
98 | elif re.match('}', line): | |
99 | break | |
100 | ||
101 | if not line.lstrip().startswith('/*'): | |
102 | fatal("unexpected syntax between ofpraw types") | |
103 | ||
104 | comment = line.lstrip()[2:].strip() | |
105 | while not comment.endswith('*/'): | |
106 | get_line() | |
107 | if line.startswith('/*') or not line or line.isspace(): | |
91148d4b | 108 | fatal("unexpected syntax within message") |
982697a4 BP |
109 | comment += ' %s' % line.lstrip('* \t').rstrip(' \t\r\n') |
110 | comment = comment[:-2].rstrip() | |
111 | ||
cf7cf630 | 112 | m = re.match(r'([A-Z]+) ([-.+\d]+|<all>) \((\d+)\): ([^.]+)\.$', comment) |
982697a4 BP |
113 | if not m: |
114 | fatal("unexpected syntax between messages") | |
115 | type_, versions, number, contents = m.groups() | |
116 | number = int(number) | |
117 | ||
118 | get_line() | |
119 | m = re.match('\s+(?:OFPRAW_%s)(\d*)_([A-Z0-9_]+),?$' % type_, | |
120 | line) | |
121 | if not m: | |
122 | fatal("syntax error expecting OFPRAW_ enum") | |
123 | vinfix, name = m.groups() | |
124 | rawname = 'OFPRAW_%s%s_%s' % (type_, vinfix, name) | |
125 | ||
42dccab5 | 126 | min_version, max_version = decode_version_range(versions) |
982697a4 BP |
127 | |
128 | human_name = '%s_%s' % (type_, name) | |
129 | if type_.endswith('ST'): | |
130 | if rawname.endswith('_REQUEST'): | |
131 | human_name = human_name[:-8] + " request" | |
132 | elif rawname.endswith('_REPLY'): | |
133 | human_name = human_name[:-6] + " reply" | |
134 | else: | |
135 | fatal("%s messages are statistics but %s doesn't end " | |
136 | "in _REQUEST or _REPLY" % (type_, rawname)) | |
137 | ||
138 | these_hdrs = [] | |
139 | for version in range(min_version, max_version + 1): | |
140 | if type_ == 'OFPT': | |
141 | if number == OFPT_VENDOR: | |
142 | fatal("OFPT (%d) is used for vendor extensions" | |
143 | % number) | |
42dccab5 | 144 | elif (version == VERSION["1.0"] |
982697a4 BP |
145 | and (number == OFPT10_STATS_REQUEST |
146 | or number == OFPT10_STATS_REPLY)): | |
147 | fatal("OFPT 1.0 (%d) is used for stats messages" | |
148 | % number) | |
42dccab5 | 149 | elif (version != VERSION["1.0"] |
982697a4 BP |
150 | and (number == OFPT11_STATS_REQUEST |
151 | or number == OFPT11_STATS_REPLY)): | |
152 | fatal("OFPT 1.1+ (%d) is used for stats messages" | |
153 | % number) | |
154 | hdrs = (version, number, 0, 0, 0) | |
155 | elif type_ == 'OFPST' and name.endswith('_REQUEST'): | |
42dccab5 | 156 | if version == VERSION["1.0"]: |
982697a4 BP |
157 | hdrs = (version, OFPT10_STATS_REQUEST, number, 0, 0) |
158 | else: | |
159 | hdrs = (version, OFPT11_STATS_REQUEST, number, 0, 0) | |
160 | elif type_ == 'OFPST' and name.endswith('_REPLY'): | |
42dccab5 | 161 | if version == VERSION["1.0"]: |
982697a4 BP |
162 | hdrs = (version, OFPT10_STATS_REPLY, number, 0, 0) |
163 | else: | |
164 | hdrs = (version, OFPT11_STATS_REPLY, number, 0, 0) | |
2123bc8c | 165 | elif type_ == 'ONFT': |
90219696 SH |
166 | hdrs = (version, OFPT_VENDOR, 0, ONF_VENDOR_ID, number) |
167 | elif type_ == 'ONFST' and name.endswith('_REQUEST'): | |
168 | if version == VERSION["1.0"]: | |
169 | hdrs = (version, OFPT10_STATS_REQUEST, OFPST_VENDOR, | |
170 | ONF_VENDOR_ID, number) | |
171 | else: | |
172 | hdrs = (version, OFPT11_STATS_REQUEST, OFPST_VENDOR, | |
173 | ONF_VENDOR_ID, number) | |
174 | elif type_ == 'ONFST' and name.endswith('_REPLY'): | |
175 | if version == VERSION["1.0"]: | |
176 | hdrs = (version, OFPT10_STATS_REPLY, OFPST_VENDOR, | |
177 | ONF_VENDOR_ID, number) | |
178 | else: | |
179 | hdrs = (version, OFPT11_STATS_REPLY, OFPST_VENDOR, | |
180 | ONF_VENDOR_ID, number) | |
982697a4 BP |
181 | elif type_ == 'NXT': |
182 | hdrs = (version, OFPT_VENDOR, 0, NX_VENDOR_ID, number) | |
183 | elif type_ == 'NXST' and name.endswith('_REQUEST'): | |
42dccab5 | 184 | if version == VERSION["1.0"]: |
982697a4 BP |
185 | hdrs = (version, OFPT10_STATS_REQUEST, OFPST_VENDOR, |
186 | NX_VENDOR_ID, number) | |
187 | else: | |
188 | hdrs = (version, OFPT11_STATS_REQUEST, OFPST_VENDOR, | |
189 | NX_VENDOR_ID, number) | |
190 | elif type_ == 'NXST' and name.endswith('_REPLY'): | |
42dccab5 | 191 | if version == VERSION["1.0"]: |
982697a4 BP |
192 | hdrs = (version, OFPT10_STATS_REPLY, OFPST_VENDOR, |
193 | NX_VENDOR_ID, number) | |
194 | else: | |
195 | hdrs = (version, OFPT11_STATS_REPLY, OFPST_VENDOR, | |
196 | NX_VENDOR_ID, number) | |
197 | else: | |
198 | fatal("type '%s' unknown" % type_) | |
199 | ||
200 | if hdrs in all_hdrs: | |
201 | error("Duplicate message definition for %s." % str(hdrs)) | |
202 | sys.stderr.write("%s: Here is the location " | |
203 | "of the previous definition.\n" | |
204 | % (all_hdrs[hdrs])) | |
205 | all_hdrs[hdrs] = here | |
206 | these_hdrs.append(hdrs) | |
207 | ||
208 | extra_multiple = '0' | |
209 | if contents == 'void': | |
210 | min_body = '0' | |
211 | else: | |
212 | min_body_elem = [] | |
213 | for c in [s.strip() for s in contents.split(",")]: | |
214 | if c.endswith('[]'): | |
215 | if extra_multiple == '0': | |
216 | extra_multiple = make_sizeof(c[:-2]) | |
217 | else: | |
218 | error("Cannot have multiple [] elements") | |
219 | else: | |
220 | min_body_elem.append(c) | |
221 | ||
222 | if min_body_elem: | |
223 | min_body = " + ".join([make_sizeof(s) | |
224 | for s in min_body_elem]) | |
225 | else: | |
226 | if extra_multiple == '0': | |
227 | error("Must specify contents (use 'void' if empty)") | |
228 | min_body = 0 | |
229 | ||
230 | if rawname in all_raws: | |
231 | fatal("%s: Duplicate name" % rawname) | |
232 | ||
233 | all_raws[rawname] = {"hdrs": these_hdrs, | |
234 | "min_version": min_version, | |
235 | "max_version": max_version, | |
236 | "min_body": min_body, | |
237 | "extra_multiple": extra_multiple, | |
238 | "type": type_, | |
239 | "human_name": human_name, | |
240 | "line": first_line_number} | |
241 | all_raws_order.append(rawname) | |
242 | ||
243 | continue | |
244 | ||
245 | while True: | |
246 | get_line() | |
247 | if re.match('enum ofptype', line): | |
248 | break | |
249 | ||
64795a0d | 250 | all_types = [] |
982697a4 BP |
251 | while True: |
252 | get_line() | |
253 | if re.match(r'\s*/?\*', line) or line.isspace(): | |
254 | continue | |
255 | elif re.match('}', line): | |
256 | break | |
257 | ||
258 | if not re.match(r'\s*OFPTYPE_.*/\*', line): | |
259 | fatal("unexpected syntax between OFPTYPE_ definitions") | |
260 | ||
261 | syntax = line.strip() | |
262 | while not syntax.endswith('*/'): | |
263 | get_line() | |
264 | if not line.strip().startswith('*'): | |
265 | fatal("unexpected syntax within OFPTYPE_ definition") | |
266 | syntax += ' %s' % line.strip().lstrip('* \t') | |
267 | syntax = syntax.strip() | |
268 | ||
269 | m = re.match(r'(OFPTYPE_[A-Z0-9_]+),\s*/\* (.*) \*/', syntax) | |
270 | if not m: | |
271 | fatal("syntax error in OFPTYPE_ definition") | |
272 | ||
273 | ofptype, raws_ = m.groups() | |
274 | raws = [s.rstrip('.') for s in raws_.split()] | |
275 | for raw in raws: | |
276 | if not re.match('OFPRAW_[A-Z0-9_]+$', raw): | |
277 | fatal("%s: invalid OFPRAW_* name syntax" % raw) | |
278 | if raw not in all_raws: | |
279 | fatal("%s: not a declared OFPRAW_* name" % raw) | |
280 | if "ofptype" in all_raws[raw]: | |
281 | fatal("%s: already part of %s" | |
282 | % (raw, all_raws[raw]["ofptype"])) | |
283 | all_raws[raw]["ofptype"] = ofptype | |
284 | ||
64795a0d BP |
285 | all_types.append(all_raws[raws[0]]["human_name"]) |
286 | ||
982697a4 BP |
287 | input_file.close() |
288 | ||
289 | if n_errors: | |
290 | sys.exit(1) | |
291 | ||
292 | output = [] | |
293 | output.append("/* Generated automatically; do not modify! " | |
294 | "-*- buffer-read-only: t -*- */") | |
295 | output.append("") | |
296 | ||
297 | for raw in all_raws_order: | |
298 | r = all_raws[raw] | |
299 | output.append("static struct raw_instance %s_instances[] = {" | |
300 | % raw.lower()) | |
301 | for hdrs in r['hdrs']: | |
302 | output.append(" { {0, NULL}, {%d, %d, %d, 0x%x, %d}, %s, 0 }," | |
303 | % (hdrs + (raw,))) | |
304 | ||
305 | output.append("};") | |
306 | ||
307 | output.append("") | |
308 | ||
309 | output.append("static struct raw_info raw_infos[] = {") | |
310 | for raw in all_raws_order: | |
311 | r = all_raws[raw] | |
312 | if "ofptype" not in r: | |
313 | error("%s: no defined OFPTYPE_" % raw) | |
314 | continue | |
315 | output.append(" {") | |
316 | output.append(" %s_instances," % raw.lower()) | |
317 | output.append(" %d, %d," % (r["min_version"], r["max_version"])) | |
318 | output.append("#line %s \"%s\"" % (r["line"], file_name)) | |
319 | output.append(" %s," % r["min_body"]) | |
320 | output.append("#line %s \"%s\"" % (r["line"], file_name)) | |
321 | output.append(" %s," % r["extra_multiple"]) | |
322 | output.append("#line %s \"%s\"" % (len(output) + 2, output_file_name)) | |
323 | output.append(" %s," % r["ofptype"]) | |
324 | output.append(" \"%s\"," % r["human_name"]) | |
325 | output.append(" },") | |
326 | ||
327 | if r['type'].endswith("ST"): | |
328 | for hdrs in r['hdrs']: | |
329 | op_hdrs = list(hdrs) | |
42dccab5 | 330 | if hdrs[0] == VERSION["1.0"]: |
982697a4 BP |
331 | if hdrs[1] == OFPT10_STATS_REQUEST: |
332 | op_hdrs[1] = OFPT10_STATS_REPLY | |
333 | elif hdrs[1] == OFPT10_STATS_REPLY: | |
334 | op_hdrs[1] = OFPT10_STATS_REQUEST | |
335 | else: | |
336 | assert False | |
337 | else: | |
338 | if hdrs[1] == OFPT11_STATS_REQUEST: | |
339 | op_hdrs[1] = OFPT11_STATS_REPLY | |
340 | elif hdrs[1] == OFPT11_STATS_REPLY: | |
341 | op_hdrs[1] = OFPT11_STATS_REQUEST | |
342 | else: | |
343 | assert False | |
344 | if tuple(op_hdrs) not in all_hdrs: | |
345 | if r["human_name"].endswith("request"): | |
346 | fatal("%s has no corresponding reply" | |
347 | % r["human_name"]) | |
348 | else: | |
349 | fatal("%s has no corresponding request" | |
350 | % r["human_name"]) | |
351 | output.append("};") | |
352 | ||
64795a0d BP |
353 | output.append(""); |
354 | output.append("static const char *type_names[] = {"); | |
355 | for t in all_types: | |
356 | output.append(" \"%s\"," % t) | |
357 | output.append("};") | |
358 | ||
982697a4 BP |
359 | if n_errors: |
360 | sys.exit(1) | |
361 | ||
362 | return output | |
363 | ||
364 | ||
365 | if __name__ == '__main__': | |
366 | if '--help' in sys.argv: | |
367 | usage() | |
368 | elif len(sys.argv) != 3: | |
91148d4b | 369 | sys.stderr.write("exactly two non-option arguments required; " |
982697a4 BP |
370 | "use --help for help\n") |
371 | sys.exit(1) | |
372 | else: | |
373 | global file_name | |
374 | global input_file | |
375 | global line_number | |
376 | file_name = sys.argv[1] | |
377 | input_file = open(file_name) | |
378 | line_number = 0 | |
379 | ||
380 | for line in extract_ofp_msgs(sys.argv[2]): | |
d34a1cc0 | 381 | print(line) |
982697a4 | 382 |