9 # Map from OpenFlow version number to version ID used in ofp_header.
10 version_map = {"1.0": 0x01,
16 version_reverse_map = dict((v, k) for (k, v) in version_map.iteritems())
18 # Map from vendor name to the length of the action header.
19 vendor_map = {"OF": (0x00000000, 4),
20 "NX": (0x00002320, 10)}
22 # Basic types used in action arguments.
24 types['uint8_t'] = {"size": 1, "align": 1, "ntoh": None, "hton": None}
25 types['ovs_be16'] = {"size": 2, "align": 2, "ntoh": "ntohs", "hton": "htons"}
26 types['ovs_be32'] = {"size": 4, "align": 4, "ntoh": "ntohl", "hton": "htonl"}
27 types['ovs_be64'] = {"size": 8, "align": 8, "ntoh": "ntohll", "hton": "htonll"}
28 types['uint16_t'] = {"size": 2, "align": 2, "ntoh": None, "hton": None}
29 types['uint32_t'] = {"size": 4, "align": 4, "ntoh": None, "hton": None}
30 types['uint64_t'] = {"size": 8, "align": 8, "ntoh": None, "hton": None}
37 return (x + (y - 1)) / y * y
44 input_file = open(file_name)
51 line = input_file.readline()
54 fatal("unexpected end of input")
60 sys.stderr.write("%s:%d: %s\n" % (file_name, line_number, msg))
68 argv0 = os.path.basename(sys.argv[0])
70 %(argv0)s, for extracting OpenFlow action data
71 usage: %(argv0)s OFP_ACTIONS.C [--prototypes | --definitions]
73 This program reads ofp-actions.c to obtain information about OpenFlow
74 actions. With --prototypes, it outputs on stdout a set of prototypes to
75 #include early in ofp-actions.c. With --definitions, it outputs on stdout
76 a set of definitions to #include late in ofp-actions.c
78 OFP_ACTIONS.C should point to lib/ofp-actions.c.\
79 ''' % {"argv0": argv0})
82 def extract_ofp_actions(fn, definitions):
88 for code, size in vendor_map.values():
98 if re.match('enum ofp_raw_action_type {', line):
103 if line.startswith('/*') or not line or line.isspace():
105 elif re.match('}', line):
108 if not line.lstrip().startswith('/*'):
109 fatal("unexpected syntax between actions")
111 comment = line.lstrip()[2:].strip()
112 while not comment.endswith('*/'):
114 if line.startswith('/*') or not line or line.isspace():
115 fatal("unexpected syntax within action")
116 comment += ' %s' % line.lstrip('* \t').rstrip(' \t\r\n')
117 comment = re.sub('\[[^]]*\]', '', comment)
118 comment = comment[:-2].rstrip()
120 m = re.match('([^:]+):\s+(.*)$', comment)
122 fatal("unexpected syntax between actions")
125 argtype = m.group(2).strip().replace('.', '', 1)
128 m = re.match(r'\s+(([A-Z]+)_RAW([0-9]*)_([A-Z0-9_]+)),?', line)
130 fatal("syntax error expecting enum value")
134 fatal("%s specified twice" % enum)
138 for dst in dsts.split(', '):
139 m = re.match(r'([A-Z]+)([0-9.]+)(\+|-[0-9.]+)?(?:\((\d+)\))(?: is deprecated \(([^)]+)\))?$', dst)
141 fatal("%r: syntax error in destination" % dst)
142 vendor_name = m.group(1)
143 version1_name = m.group(2)
144 version2_name = m.group(3)
145 type_ = int(m.group(4))
146 deprecation = m.group(5)
148 if vendor_name not in vendor_map:
149 fatal("%s: unknown vendor" % vendor_name)
150 vendor = vendor_map[vendor_name][0]
152 if version1_name not in version_map:
153 fatal("%s: unknown OpenFlow version" % version1_name)
154 v1 = version_map[version1_name]
156 if version2_name is None:
158 elif version2_name == "+":
159 v2 = max(version_map.values())
160 elif version2_name[1:] not in version_map:
161 fatal("%s: unknown OpenFlow version" % version2_name[1:])
163 v2 = version_map[version2_name[1:]]
166 fatal("%s%s: %s precedes %s"
167 % (version1_name, version2_name,
168 version2_name, version1_name))
170 for version in range(v1, v2 + 1):
171 domain[vendor].setdefault(type_, {})
172 if version in domain[vendor][type_]:
173 v = domain[vendor][type_][version]
174 msg = "%#x,%d in OF%s means both %s and %s" % (
175 vendor, type_, version_reverse_map[version],
177 error("%s: %s." % (dst, msg))
178 sys.stderr.write("%s:%d: %s: Here is the location "
179 "of the previous definition.\n"
180 % (v["file_name"], v["line_number"],
184 header_len = vendor_map[vendor_name][1]
186 base_argtype = argtype.replace(', ..', '', 1)
187 if base_argtype in types:
188 arg_align = types[base_argtype]['align']
189 arg_len = types[base_argtype]['size']
190 arg_ofs = round_up(header_len, arg_align)
191 min_length = round_up(arg_ofs + arg_len,
193 elif base_argtype == 'void':
194 min_length = round_up(header_len, OFP_ACTION_ALIGN)
197 elif re.match(r'struct [a-zA-Z0-9_]+$', base_argtype):
198 min_length = 'sizeof(%s)' % base_argtype
199 arg_structs.add(base_argtype)
202 # should also emit OFP_ACTION_ALIGN assertion
204 fatal("bad argument type %s" % argtype)
206 ellipsis = argtype != base_argtype
208 max_length = '65536 - OFP_ACTION_ALIGN'
210 max_length = min_length
212 info = {"enum": enum, # 0
213 "deprecation": deprecation, # 1
214 "file_name": file_name, # 2
215 "line_number": line_number, # 3
216 "min_length": min_length, # 4
217 "max_length": max_length, # 5
218 "arg_ofs": arg_ofs, # 6
219 "arg_len": arg_len, # 7
220 "base_argtype": base_argtype, # 8
221 "version": version, # 9
223 domain[vendor][type_][version] = info
225 enums.setdefault(enum, [])
226 enums[enum].append(info)
234 /* Generated automatically; do not modify! -*- buffer-read-only: t -*- */
238 print "/* Verify that structs used as actions are reasonable sizes. */"
239 for s in sorted(arg_structs):
240 print "BUILD_ASSERT_DECL(sizeof(%s) %% OFP_ACTION_ALIGN == 0);" % s
242 print "\nstatic struct ofpact_raw_instance all_raw_instances[] = {"
243 for vendor in domain:
244 for type_ in domain[vendor]:
245 for version in domain[vendor][type_]:
246 d = domain[vendor][type_][version]
247 print " { { 0x%08x, %2d, 0x%02x }, " % (
248 vendor, type_, version)
249 print " %s," % d["enum"]
250 print " HMAP_NODE_NULL_INITIALIZER,"
251 print " HMAP_NODE_NULL_INITIALIZER,"
252 print " %s," % d["min_length"]
253 print " %s," % d["max_length"]
254 print " %s," % d["arg_ofs"]
255 print " %s," % d["arg_len"]
256 print " \"%s\"," % re.sub('_RAW[0-9]*', '', d["enum"], 1)
258 print " \"%s\"," % re.sub(r'(["\\])', r'\\\1', d["deprecation"])
264 for versions in enums.values():
265 need_ofp_version = False
267 assert v["arg_len"] == versions[0]["arg_len"]
268 assert v["base_argtype"] == versions[0]["base_argtype"]
269 if (v["min_length"] != versions[0]["min_length"] or
270 v["arg_ofs"] != versions[0]["arg_ofs"] or
271 v["type"] != versions[0]["type"]):
272 need_ofp_version = True
273 base_argtype = versions[0]["base_argtype"]
275 decl = "static inline "
276 if base_argtype.startswith('struct'):
277 decl += "%s *" %base_argtype
280 decl += "\nput_%s(struct ofpbuf *openflow" % versions[0]["enum"].replace('_RAW', '', 1)
282 decl += ", enum ofp_version version"
283 if base_argtype != 'void' and not base_argtype.startswith('struct'):
284 decl += ", %s arg" % base_argtype
289 if base_argtype.startswith('struct'):
291 decl += "ofpact_put_raw(openflow, "
295 decl += "%s" % versions[0]["version"]
296 decl += ", %s, " % versions[0]["enum"]
297 if base_argtype.startswith('struct') or base_argtype == 'void':
300 ntoh = types[base_argtype]['ntoh']
302 decl += "%s(arg)" % ntoh
315 ofpact_decode(const struct ofp_action_header *a, enum ofp_raw_action_type raw,
316 uint64_t arg, struct ofpbuf *out)
320 for versions in enums.values():
321 enum = versions[0]["enum"]
322 print " case %s:" % enum
323 base_argtype = versions[0]["base_argtype"]
324 if base_argtype == 'void':
325 print " return decode_%s(out);" % enum
327 if base_argtype.startswith('struct'):
328 arg = "ALIGNED_CAST(const %s *, a)" % base_argtype
330 hton = types[base_argtype]['hton']
332 arg = "%s(arg)" % hton
335 print " return decode_%s(%s, out);" % (enum, arg)
344 for versions in enums.values():
345 enum = versions[0]["enum"]
346 prototype = "static enum ofperr decode_%s(" % enum
347 base_argtype = versions[0]["base_argtype"]
348 if base_argtype != 'void':
349 if base_argtype.startswith('struct'):
350 prototype += "const %s *, " % base_argtype
352 prototype += "%s, " % base_argtype
353 prototype += "struct ofpbuf *);"
357 static enum ofperr ofpact_decode(const struct ofp_action_header *,
358 enum ofp_raw_action_type raw,
359 uint64_t arg, struct ofpbuf *out);
362 if __name__ == '__main__':
363 if '--help' in sys.argv:
365 elif len(sys.argv) != 3:
366 sys.stderr.write("exactly two arguments required; "
367 "use --help for help\n")
369 elif sys.argv[2] == '--prototypes':
370 extract_ofp_actions(sys.argv[1], False)
371 elif sys.argv[2] == '--definitions':
372 extract_ofp_actions(sys.argv[1], True)
374 sys.stderr.write("invalid arguments; use --help for help\n")