]>
Commit | Line | Data |
---|---|---|
c2d936a4 BP |
1 | #! /usr/bin/python |
2 | ||
3 | import sys | |
4 | import os.path | |
5 | import re | |
6 | ||
7 | OFP_ACTION_ALIGN = 8 | |
8 | ||
9 | # Map from OpenFlow version number to version ID used in ofp_header. | |
10 | version_map = {"1.0": 0x01, | |
11 | "1.1": 0x02, | |
12 | "1.2": 0x03, | |
13 | "1.3": 0x04, | |
14 | "1.4": 0x05, | |
15 | "1.5": 0x06} | |
16 | version_reverse_map = dict((v, k) for (k, v) in version_map.iteritems()) | |
17 | ||
18 | # Map from vendor name to the length of the action header. | |
19 | vendor_map = {"OF": (0x00000000, 4), | |
914624f8 | 20 | "ONF": (0x4f4e4600, 10), |
c2d936a4 BP |
21 | "NX": (0x00002320, 10)} |
22 | ||
23 | # Basic types used in action arguments. | |
24 | types = {} | |
25 | types['uint8_t'] = {"size": 1, "align": 1, "ntoh": None, "hton": None} | |
26 | types['ovs_be16'] = {"size": 2, "align": 2, "ntoh": "ntohs", "hton": "htons"} | |
27 | types['ovs_be32'] = {"size": 4, "align": 4, "ntoh": "ntohl", "hton": "htonl"} | |
28 | types['ovs_be64'] = {"size": 8, "align": 8, "ntoh": "ntohll", "hton": "htonll"} | |
29 | types['uint16_t'] = {"size": 2, "align": 2, "ntoh": None, "hton": None} | |
30 | types['uint32_t'] = {"size": 4, "align": 4, "ntoh": None, "hton": None} | |
31 | types['uint64_t'] = {"size": 8, "align": 8, "ntoh": None, "hton": None} | |
32 | ||
33 | line = "" | |
34 | ||
35 | arg_structs = set() | |
36 | ||
37 | def round_up(x, y): | |
38 | return (x + (y - 1)) / y * y | |
39 | ||
40 | def open_file(fn): | |
41 | global file_name | |
42 | global input_file | |
43 | global line_number | |
44 | file_name = fn | |
45 | input_file = open(file_name) | |
46 | line_number = 0 | |
47 | ||
48 | def get_line(): | |
49 | global input_file | |
50 | global line | |
51 | global line_number | |
52 | line = input_file.readline() | |
53 | line_number += 1 | |
54 | if line == "": | |
55 | fatal("unexpected end of input") | |
56 | return line | |
57 | ||
58 | n_errors = 0 | |
59 | def error(msg): | |
60 | global n_errors | |
61 | sys.stderr.write("%s:%d: %s\n" % (file_name, line_number, msg)) | |
62 | n_errors += 1 | |
63 | ||
64 | def fatal(msg): | |
65 | error(msg) | |
66 | sys.exit(1) | |
67 | ||
68 | def usage(): | |
69 | argv0 = os.path.basename(sys.argv[0]) | |
70 | print ('''\ | |
71 | %(argv0)s, for extracting OpenFlow action data | |
72 | usage: %(argv0)s OFP_ACTIONS.C [--prototypes | --definitions] | |
73 | ||
74 | This program reads ofp-actions.c to obtain information about OpenFlow | |
75 | actions. With --prototypes, it outputs on stdout a set of prototypes to | |
76 | #include early in ofp-actions.c. With --definitions, it outputs on stdout | |
77 | a set of definitions to #include late in ofp-actions.c | |
78 | ||
79 | OFP_ACTIONS.C should point to lib/ofp-actions.c.\ | |
80 | ''' % {"argv0": argv0}) | |
81 | sys.exit(0) | |
82 | ||
83 | def extract_ofp_actions(fn, definitions): | |
84 | error_types = {} | |
85 | ||
86 | comments = [] | |
87 | names = [] | |
88 | domain = {} | |
89 | for code, size in vendor_map.values(): | |
90 | domain[code] = {} | |
91 | enums = {} | |
92 | ||
93 | n_errors = 0 | |
94 | ||
95 | open_file(fn) | |
96 | ||
97 | while True: | |
98 | get_line() | |
99 | if re.match('enum ofp_raw_action_type {', line): | |
100 | break | |
101 | ||
102 | while True: | |
103 | get_line() | |
104 | if line.startswith('/*') or not line or line.isspace(): | |
105 | continue | |
106 | elif re.match('}', line): | |
107 | break | |
108 | ||
109 | if not line.lstrip().startswith('/*'): | |
110 | fatal("unexpected syntax between actions") | |
111 | ||
112 | comment = line.lstrip()[2:].strip() | |
113 | while not comment.endswith('*/'): | |
114 | get_line() | |
115 | if line.startswith('/*') or not line or line.isspace(): | |
116 | fatal("unexpected syntax within action") | |
117 | comment += ' %s' % line.lstrip('* \t').rstrip(' \t\r\n') | |
118 | comment = re.sub('\[[^]]*\]', '', comment) | |
119 | comment = comment[:-2].rstrip() | |
120 | ||
121 | m = re.match('([^:]+):\s+(.*)$', comment) | |
122 | if not m: | |
123 | fatal("unexpected syntax between actions") | |
124 | ||
125 | dsts = m.group(1) | |
126 | argtype = m.group(2).strip().replace('.', '', 1) | |
127 | ||
128 | get_line() | |
129 | m = re.match(r'\s+(([A-Z]+)_RAW([0-9]*)_([A-Z0-9_]+)),?', line) | |
130 | if not m: | |
131 | fatal("syntax error expecting enum value") | |
132 | ||
133 | enum = m.group(1) | |
134 | if enum in names: | |
135 | fatal("%s specified twice" % enum) | |
136 | ||
137 | names.append(enum) | |
138 | ||
139 | for dst in dsts.split(', '): | |
140 | m = re.match(r'([A-Z]+)([0-9.]+)(\+|-[0-9.]+)?(?:\((\d+)\))(?: is deprecated \(([^)]+)\))?$', dst) | |
141 | if not m: | |
142 | fatal("%r: syntax error in destination" % dst) | |
143 | vendor_name = m.group(1) | |
144 | version1_name = m.group(2) | |
145 | version2_name = m.group(3) | |
146 | type_ = int(m.group(4)) | |
147 | deprecation = m.group(5) | |
148 | ||
149 | if vendor_name not in vendor_map: | |
150 | fatal("%s: unknown vendor" % vendor_name) | |
151 | vendor = vendor_map[vendor_name][0] | |
152 | ||
153 | if version1_name not in version_map: | |
154 | fatal("%s: unknown OpenFlow version" % version1_name) | |
155 | v1 = version_map[version1_name] | |
156 | ||
157 | if version2_name is None: | |
158 | v2 = v1 | |
159 | elif version2_name == "+": | |
160 | v2 = max(version_map.values()) | |
161 | elif version2_name[1:] not in version_map: | |
162 | fatal("%s: unknown OpenFlow version" % version2_name[1:]) | |
163 | else: | |
164 | v2 = version_map[version2_name[1:]] | |
165 | ||
166 | if v2 < v1: | |
167 | fatal("%s%s: %s precedes %s" | |
168 | % (version1_name, version2_name, | |
169 | version2_name, version1_name)) | |
170 | ||
171 | for version in range(v1, v2 + 1): | |
172 | domain[vendor].setdefault(type_, {}) | |
173 | if version in domain[vendor][type_]: | |
174 | v = domain[vendor][type_][version] | |
175 | msg = "%#x,%d in OF%s means both %s and %s" % ( | |
176 | vendor, type_, version_reverse_map[version], | |
177 | v["enum"], enum) | |
178 | error("%s: %s." % (dst, msg)) | |
179 | sys.stderr.write("%s:%d: %s: Here is the location " | |
180 | "of the previous definition.\n" | |
181 | % (v["file_name"], v["line_number"], | |
182 | dst)) | |
183 | n_errors += 1 | |
184 | else: | |
185 | header_len = vendor_map[vendor_name][1] | |
186 | ||
187 | base_argtype = argtype.replace(', ..', '', 1) | |
188 | if base_argtype in types: | |
189 | arg_align = types[base_argtype]['align'] | |
190 | arg_len = types[base_argtype]['size'] | |
191 | arg_ofs = round_up(header_len, arg_align) | |
192 | min_length = round_up(arg_ofs + arg_len, | |
193 | OFP_ACTION_ALIGN) | |
194 | elif base_argtype == 'void': | |
195 | min_length = round_up(header_len, OFP_ACTION_ALIGN) | |
196 | arg_len = 0 | |
197 | arg_ofs = 0 | |
198 | elif re.match(r'struct [a-zA-Z0-9_]+$', base_argtype): | |
199 | min_length = 'sizeof(%s)' % base_argtype | |
200 | arg_structs.add(base_argtype) | |
201 | arg_len = 0 | |
202 | arg_ofs = 0 | |
203 | # should also emit OFP_ACTION_ALIGN assertion | |
204 | else: | |
205 | fatal("bad argument type %s" % argtype) | |
206 | ||
207 | ellipsis = argtype != base_argtype | |
208 | if ellipsis: | |
209 | max_length = '65536 - OFP_ACTION_ALIGN' | |
210 | else: | |
211 | max_length = min_length | |
212 | ||
213 | info = {"enum": enum, # 0 | |
214 | "deprecation": deprecation, # 1 | |
215 | "file_name": file_name, # 2 | |
216 | "line_number": line_number, # 3 | |
217 | "min_length": min_length, # 4 | |
218 | "max_length": max_length, # 5 | |
219 | "arg_ofs": arg_ofs, # 6 | |
220 | "arg_len": arg_len, # 7 | |
221 | "base_argtype": base_argtype, # 8 | |
222 | "version": version, # 9 | |
223 | "type": type_} # 10 | |
224 | domain[vendor][type_][version] = info | |
225 | ||
226 | enums.setdefault(enum, []) | |
227 | enums[enum].append(info) | |
228 | ||
229 | input_file.close() | |
230 | ||
231 | if n_errors: | |
232 | sys.exit(1) | |
233 | ||
234 | print """\ | |
235 | /* Generated automatically; do not modify! -*- buffer-read-only: t -*- */ | |
236 | """ | |
237 | ||
238 | if definitions: | |
239 | print "/* Verify that structs used as actions are reasonable sizes. */" | |
240 | for s in sorted(arg_structs): | |
241 | print "BUILD_ASSERT_DECL(sizeof(%s) %% OFP_ACTION_ALIGN == 0);" % s | |
242 | ||
243 | print "\nstatic struct ofpact_raw_instance all_raw_instances[] = {" | |
244 | for vendor in domain: | |
245 | for type_ in domain[vendor]: | |
246 | for version in domain[vendor][type_]: | |
247 | d = domain[vendor][type_][version] | |
248 | print " { { 0x%08x, %2d, 0x%02x }, " % ( | |
249 | vendor, type_, version) | |
250 | print " %s," % d["enum"] | |
251 | print " HMAP_NODE_NULL_INITIALIZER," | |
252 | print " HMAP_NODE_NULL_INITIALIZER," | |
253 | print " %s," % d["min_length"] | |
254 | print " %s," % d["max_length"] | |
255 | print " %s," % d["arg_ofs"] | |
256 | print " %s," % d["arg_len"] | |
257 | print " \"%s\"," % re.sub('_RAW[0-9]*', '', d["enum"], 1) | |
258 | if d["deprecation"]: | |
259 | print " \"%s\"," % re.sub(r'(["\\])', r'\\\1', d["deprecation"]) | |
260 | else: | |
261 | print " NULL," | |
262 | print " }," | |
263 | print "};"; | |
264 | ||
265 | for versions in enums.values(): | |
266 | need_ofp_version = False | |
267 | for v in versions: | |
268 | assert v["arg_len"] == versions[0]["arg_len"] | |
269 | assert v["base_argtype"] == versions[0]["base_argtype"] | |
270 | if (v["min_length"] != versions[0]["min_length"] or | |
271 | v["arg_ofs"] != versions[0]["arg_ofs"] or | |
272 | v["type"] != versions[0]["type"]): | |
273 | need_ofp_version = True | |
274 | base_argtype = versions[0]["base_argtype"] | |
275 | ||
276 | decl = "static inline " | |
277 | if base_argtype.startswith('struct'): | |
278 | decl += "%s *" %base_argtype | |
279 | else: | |
280 | decl += "void" | |
281 | decl += "\nput_%s(struct ofpbuf *openflow" % versions[0]["enum"].replace('_RAW', '', 1) | |
282 | if need_ofp_version: | |
283 | decl += ", enum ofp_version version" | |
284 | if base_argtype != 'void' and not base_argtype.startswith('struct'): | |
285 | decl += ", %s arg" % base_argtype | |
286 | decl += ")" | |
287 | if definitions: | |
288 | decl += "{\n" | |
289 | decl += " " | |
290 | if base_argtype.startswith('struct'): | |
291 | decl += "return " | |
292 | decl += "ofpact_put_raw(openflow, " | |
293 | if need_ofp_version: | |
294 | decl += "version" | |
295 | else: | |
296 | decl += "%s" % versions[0]["version"] | |
297 | decl += ", %s, " % versions[0]["enum"] | |
298 | if base_argtype.startswith('struct') or base_argtype == 'void': | |
299 | decl += "0" | |
300 | else: | |
301 | ntoh = types[base_argtype]['ntoh'] | |
302 | if ntoh: | |
303 | decl += "%s(arg)" % ntoh | |
304 | else: | |
305 | decl += "arg" | |
306 | decl += ");\n" | |
307 | decl += "}" | |
308 | else: | |
309 | decl += ";" | |
310 | print decl | |
311 | ||
312 | ||
313 | if definitions: | |
314 | print """\ | |
315 | static enum ofperr | |
316 | ofpact_decode(const struct ofp_action_header *a, enum ofp_raw_action_type raw, | |
317 | uint64_t arg, struct ofpbuf *out) | |
318 | { | |
319 | switch (raw) {\ | |
320 | """ | |
321 | for versions in enums.values(): | |
322 | enum = versions[0]["enum"] | |
323 | print " case %s:" % enum | |
324 | base_argtype = versions[0]["base_argtype"] | |
325 | if base_argtype == 'void': | |
326 | print " return decode_%s(out);" % enum | |
327 | else: | |
328 | if base_argtype.startswith('struct'): | |
329 | arg = "ALIGNED_CAST(const %s *, a)" % base_argtype | |
330 | else: | |
331 | hton = types[base_argtype]['hton'] | |
332 | if hton: | |
333 | arg = "%s(arg)" % hton | |
334 | else: | |
335 | arg = "arg" | |
336 | print " return decode_%s(%s, out);" % (enum, arg) | |
337 | ||
338 | print """\ | |
339 | default: | |
340 | OVS_NOT_REACHED(); | |
341 | } | |
342 | }\ | |
343 | """ | |
344 | else: | |
345 | for versions in enums.values(): | |
346 | enum = versions[0]["enum"] | |
347 | prototype = "static enum ofperr decode_%s(" % enum | |
348 | base_argtype = versions[0]["base_argtype"] | |
349 | if base_argtype != 'void': | |
350 | if base_argtype.startswith('struct'): | |
351 | prototype += "const %s *, " % base_argtype | |
352 | else: | |
353 | prototype += "%s, " % base_argtype | |
354 | prototype += "struct ofpbuf *);" | |
355 | print prototype | |
356 | ||
357 | print """ | |
358 | static enum ofperr ofpact_decode(const struct ofp_action_header *, | |
359 | enum ofp_raw_action_type raw, | |
360 | uint64_t arg, struct ofpbuf *out); | |
361 | """ | |
362 | ||
363 | if __name__ == '__main__': | |
364 | if '--help' in sys.argv: | |
365 | usage() | |
366 | elif len(sys.argv) != 3: | |
367 | sys.stderr.write("exactly two arguments required; " | |
368 | "use --help for help\n") | |
369 | sys.exit(1) | |
370 | elif sys.argv[2] == '--prototypes': | |
371 | extract_ofp_actions(sys.argv[1], False) | |
372 | elif sys.argv[2] == '--definitions': | |
373 | extract_ofp_actions(sys.argv[1], True) | |
374 | else: | |
375 | sys.stderr.write("invalid arguments; use --help for help\n") | |
376 | sys.exit(1) | |
377 |