]> git.proxmox.com Git - mirror_ovs.git/blob - build-aux/extract-ofp-actions
Merge branch 'dpdk_merge' of https://github.com/istokes/ovs into HEAD
[mirror_ovs.git] / build-aux / extract-ofp-actions
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.items())
17
18 # Map from vendor name to the length of the action header.
19 vendor_map = {"OF": (0x00000000, 4),
20 "ONF": (0x4f4e4600, 10),
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 int((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 argtypes = m.group(2).strip().replace('.', '', 1)
127
128 if 'VLMFF' in argtypes:
129 arg_vl_mff_map = True
130 else:
131 arg_vl_mff_map = False
132 argtype = argtypes.replace('VLMFF', '', 1).rstrip()
133
134 get_line()
135 m = re.match(r'\s+(([A-Z]+)_RAW([0-9]*)_([A-Z0-9_]+)),?', line)
136 if not m:
137 fatal("syntax error expecting enum value")
138
139 enum = m.group(1)
140 if enum in names:
141 fatal("%s specified twice" % enum)
142
143 names.append(enum)
144
145 for dst in dsts.split(', '):
146 m = re.match(r'([A-Z]+)([0-9.]+)(\+|-[0-9.]+)?(?:\((\d+)\))(?: is deprecated \(([^)]+)\))?$', dst)
147 if not m:
148 fatal("%r: syntax error in destination" % dst)
149 vendor_name = m.group(1)
150 version1_name = m.group(2)
151 version2_name = m.group(3)
152 type_ = int(m.group(4))
153 deprecation = m.group(5)
154
155 if vendor_name not in vendor_map:
156 fatal("%s: unknown vendor" % vendor_name)
157 vendor = vendor_map[vendor_name][0]
158
159 if version1_name not in version_map:
160 fatal("%s: unknown OpenFlow version" % version1_name)
161 v1 = version_map[version1_name]
162
163 if version2_name is None:
164 v2 = v1
165 elif version2_name == "+":
166 v2 = max(version_map.values())
167 elif version2_name[1:] not in version_map:
168 fatal("%s: unknown OpenFlow version" % version2_name[1:])
169 else:
170 v2 = version_map[version2_name[1:]]
171
172 if v2 < v1:
173 fatal("%s%s: %s precedes %s"
174 % (version1_name, version2_name,
175 version2_name, version1_name))
176
177 for version in range(v1, v2 + 1):
178 domain[vendor].setdefault(type_, {})
179 if version in domain[vendor][type_]:
180 v = domain[vendor][type_][version]
181 msg = "%#x,%d in OF%s means both %s and %s" % (
182 vendor, type_, version_reverse_map[version],
183 v["enum"], enum)
184 error("%s: %s." % (dst, msg))
185 sys.stderr.write("%s:%d: %s: Here is the location "
186 "of the previous definition.\n"
187 % (v["file_name"], v["line_number"],
188 dst))
189 n_errors += 1
190 else:
191 header_len = vendor_map[vendor_name][1]
192
193 base_argtype = argtype.replace(', ..', '', 1)
194 if base_argtype in types:
195 arg_align = types[base_argtype]['align']
196 arg_len = types[base_argtype]['size']
197 arg_ofs = round_up(header_len, arg_align)
198 min_length = round_up(arg_ofs + arg_len,
199 OFP_ACTION_ALIGN)
200 elif base_argtype == 'void':
201 min_length = round_up(header_len, OFP_ACTION_ALIGN)
202 arg_len = 0
203 arg_ofs = 0
204 elif re.match(r'struct [a-zA-Z0-9_]+$', base_argtype):
205 min_length = 'sizeof(%s)' % base_argtype
206 arg_structs.add(base_argtype)
207 arg_len = 0
208 arg_ofs = 0
209 # should also emit OFP_ACTION_ALIGN assertion
210 else:
211 fatal("bad argument type %s" % argtype)
212
213 ellipsis = argtype != base_argtype
214 if ellipsis:
215 max_length = '65536 - OFP_ACTION_ALIGN'
216 else:
217 max_length = min_length
218
219 info = {"enum": enum, # 0
220 "deprecation": deprecation, # 1
221 "file_name": file_name, # 2
222 "line_number": line_number, # 3
223 "min_length": min_length, # 4
224 "max_length": max_length, # 5
225 "arg_ofs": arg_ofs, # 6
226 "arg_len": arg_len, # 7
227 "base_argtype": base_argtype, # 8
228 "arg_vl_mff_map": arg_vl_mff_map, # 9
229 "version": version, # 10
230 "type": type_} # 11
231 domain[vendor][type_][version] = info
232
233 enums.setdefault(enum, [])
234 enums[enum].append(info)
235
236 input_file.close()
237
238 if n_errors:
239 sys.exit(1)
240
241 print("""\
242 /* Generated automatically; do not modify! -*- buffer-read-only: t -*- */
243 """)
244
245 if definitions:
246 print("/* Verify that structs used as actions are reasonable sizes. */")
247 for s in sorted(arg_structs):
248 print("BUILD_ASSERT_DECL(sizeof(%s) %% OFP_ACTION_ALIGN == 0);" % s)
249
250 print("\nstatic struct ofpact_raw_instance all_raw_instances[] = {")
251 for vendor in domain:
252 for type_ in domain[vendor]:
253 for version in domain[vendor][type_]:
254 d = domain[vendor][type_][version]
255 print(" { { 0x%08x, %2d, 0x%02x }, " % (
256 vendor, type_, version))
257 print(" %s," % d["enum"])
258 print(" HMAP_NODE_NULL_INITIALIZER,")
259 print(" HMAP_NODE_NULL_INITIALIZER,")
260 print(" %s," % d["min_length"])
261 print(" %s," % d["max_length"])
262 print(" %s," % d["arg_ofs"])
263 print(" %s," % d["arg_len"])
264 print(" \"%s\"," % re.sub('_RAW[0-9]*', '', d["enum"], 1))
265 if d["deprecation"]:
266 print(" \"%s\"," % re.sub(r'(["\\])', r'\\\1', d["deprecation"]))
267 else:
268 print(" NULL,")
269 print(" },")
270 print("};")
271
272 for versions in enums.values():
273 need_ofp_version = False
274 for v in versions:
275 assert v["arg_len"] == versions[0]["arg_len"]
276 assert v["base_argtype"] == versions[0]["base_argtype"]
277 if (v["min_length"] != versions[0]["min_length"] or
278 v["arg_ofs"] != versions[0]["arg_ofs"] or
279 v["type"] != versions[0]["type"]):
280 need_ofp_version = True
281 base_argtype = versions[0]["base_argtype"]
282
283 decl = "static inline "
284 if base_argtype.startswith('struct'):
285 decl += "%s *" %base_argtype
286 else:
287 decl += "void"
288 decl += "\nput_%s(struct ofpbuf *openflow" % versions[0]["enum"].replace('_RAW', '', 1)
289 if need_ofp_version:
290 decl += ", enum ofp_version version"
291 if base_argtype != 'void' and not base_argtype.startswith('struct'):
292 decl += ", %s arg" % base_argtype
293 decl += ")"
294 if definitions:
295 decl += "{\n"
296 decl += " "
297 if base_argtype.startswith('struct'):
298 decl += "return "
299 decl += "ofpact_put_raw(openflow, "
300 if need_ofp_version:
301 decl += "version"
302 else:
303 decl += "%s" % versions[0]["version"]
304 decl += ", %s, " % versions[0]["enum"]
305 if base_argtype.startswith('struct') or base_argtype == 'void':
306 decl += "0"
307 else:
308 ntoh = types[base_argtype]['ntoh']
309 if ntoh:
310 decl += "%s(arg)" % ntoh
311 else:
312 decl += "arg"
313 decl += ");\n"
314 decl += "}"
315 else:
316 decl += ";"
317 print(decl)
318 print("")
319
320 if definitions:
321 print("""\
322 static enum ofperr
323 ofpact_decode(const struct ofp_action_header *a, enum ofp_raw_action_type raw,
324 enum ofp_version version, uint64_t arg,
325 const struct vl_mff_map *vl_mff_map,
326 uint64_t *tlv_bitmap, struct ofpbuf *out)
327 {
328 switch (raw) {\
329 """)
330 for versions in enums.values():
331 enum = versions[0]["enum"]
332 print(" case %s:" % enum)
333 base_argtype = versions[0]["base_argtype"]
334 arg_vl_mff_map = versions[0]["arg_vl_mff_map"]
335 if base_argtype == 'void':
336 print(" return decode_%s(out);" % enum)
337 else:
338 if base_argtype.startswith('struct'):
339 arg = "ALIGNED_CAST(const %s *, a)" % base_argtype
340 else:
341 hton = types[base_argtype]['hton']
342 if hton:
343 arg = "%s(arg)" % hton
344 else:
345 arg = "arg"
346 if arg_vl_mff_map:
347 print(" return decode_%s(%s, version, vl_mff_map, tlv_bitmap, out);" % (enum, arg))
348 else:
349 print(" return decode_%s(%s, version, out);" % (enum, arg))
350 print("")
351 print("""\
352 default:
353 OVS_NOT_REACHED();
354 }
355 }\
356 """)
357 else:
358 for versions in enums.values():
359 enum = versions[0]["enum"]
360 prototype = "static enum ofperr decode_%s(" % enum
361 base_argtype = versions[0]["base_argtype"]
362 arg_vl_mff_map = versions[0]["arg_vl_mff_map"]
363 if base_argtype != 'void':
364 if base_argtype.startswith('struct'):
365 prototype += "const %s *, enum ofp_version, " % base_argtype
366 else:
367 prototype += "%s, enum ofp_version, " % base_argtype
368 if arg_vl_mff_map:
369 prototype += 'const struct vl_mff_map *, uint64_t *, '
370 prototype += "struct ofpbuf *);"
371 print(prototype)
372
373 print("""
374 static enum ofperr ofpact_decode(const struct ofp_action_header *,
375 enum ofp_raw_action_type raw,
376 enum ofp_version version,
377 uint64_t arg, const struct vl_mff_map *vl_mff_map,
378 uint64_t *tlv_bitmap, struct ofpbuf *out);
379 """)
380
381 if __name__ == '__main__':
382 if '--help' in sys.argv:
383 usage()
384 elif len(sys.argv) != 3:
385 sys.stderr.write("exactly two arguments required; "
386 "use --help for help\n")
387 sys.exit(1)
388 elif sys.argv[2] == '--prototypes':
389 extract_ofp_actions(sys.argv[1], False)
390 elif sys.argv[2] == '--definitions':
391 extract_ofp_actions(sys.argv[1], True)
392 else:
393 sys.stderr.write("invalid arguments; use --help for help\n")
394 sys.exit(1)
395