9 # Maps from user-friendly version number to its protocol encoding.
10 VERSION = {"1.0": 0x01,
18 NX_VENDOR_ID = 0x00002320
19 ONF_VENDOR_ID = 0x4f4e4600
22 OFPT10_STATS_REQUEST = 16
23 OFPT10_STATS_REPLY = 17
24 OFPT11_STATS_REQUEST = 18
25 OFPT11_STATS_REPLY = 19
28 def decode_version_range(range):
30 return (VERSION[range], VERSION[range])
31 elif range.endswith('+'):
32 return (VERSION[range[:-1]], max(VERSION.values()))
33 elif range == '<all>':
36 a, b = re.match(r'^([^-]+)-([^-]+)$', range).groups()
37 return (VERSION[a], VERSION[b])
42 line = input_file.readline()
45 fatal("unexpected end of input")
50 sys.stderr.write("%s:%d: %s\n" % (file_name, line_number, msg))
58 argv0 = os.path.basename(sys.argv[0])
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.\
66 ''' % {"argv0": argv0}
70 m = re.match(r'(.*) up to (.*)', s)
72 struct, member = m.groups()
73 return "offsetof(%s, %s)" % (struct, member)
75 return "sizeof(%s)" % s
77 def extract_ofp_msgs(output_file_name):
86 if re.match('enum ofpraw', line):
91 first_line_number = line_number
92 here = '%s:%d' % (file_name, line_number)
93 if (line.startswith('/*')
94 or line.startswith(' *')
98 elif re.match('}', line):
101 if not line.lstrip().startswith('/*'):
102 fatal("unexpected syntax between ofpraw types")
104 comment = line.lstrip()[2:].strip()
105 while not comment.endswith('*/'):
107 if line.startswith('/*') or not line or line.isspace():
108 fatal("unexpected syntax within message")
109 comment += ' %s' % line.lstrip('* \t').rstrip(' \t\r\n')
110 comment = comment[:-2].rstrip()
112 m = re.match(r'([A-Z]+) ([-.+\d]+|<all>) \((\d+)\): ([^.]+)\.$', comment)
114 fatal("unexpected syntax between messages")
115 type_, versions, number, contents = m.groups()
119 m = re.match('\s+(?:OFPRAW_%s)(\d*)_([A-Z0-9_]+),?$' % type_,
122 fatal("syntax error expecting OFPRAW_ enum")
123 vinfix, name = m.groups()
124 rawname = 'OFPRAW_%s%s_%s' % (type_, vinfix, name)
126 min_version, max_version = decode_version_range(versions)
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"
135 fatal("%s messages are statistics but %s doesn't end "
136 "in _REQUEST or _REPLY" % (type_, rawname))
139 for version in range(min_version, max_version + 1):
141 if number == OFPT_VENDOR:
142 fatal("OFPT (%d) is used for vendor extensions"
144 elif (version == VERSION["1.0"]
145 and (number == OFPT10_STATS_REQUEST
146 or number == OFPT10_STATS_REPLY)):
147 fatal("OFPT 1.0 (%d) is used for stats messages"
149 elif (version != VERSION["1.0"]
150 and (number == OFPT11_STATS_REQUEST
151 or number == OFPT11_STATS_REPLY)):
152 fatal("OFPT 1.1+ (%d) is used for stats messages"
154 hdrs = (version, number, 0, 0, 0)
155 elif type_ == 'OFPST' and name.endswith('_REQUEST'):
156 if version == VERSION["1.0"]:
157 hdrs = (version, OFPT10_STATS_REQUEST, number, 0, 0)
159 hdrs = (version, OFPT11_STATS_REQUEST, number, 0, 0)
160 elif type_ == 'OFPST' and name.endswith('_REPLY'):
161 if version == VERSION["1.0"]:
162 hdrs = (version, OFPT10_STATS_REPLY, number, 0, 0)
164 hdrs = (version, OFPT11_STATS_REPLY, number, 0, 0)
165 elif type_ == 'ONFT':
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)
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)
179 hdrs = (version, OFPT11_STATS_REPLY, OFPST_VENDOR,
180 ONF_VENDOR_ID, number)
182 hdrs = (version, OFPT_VENDOR, 0, NX_VENDOR_ID, number)
183 elif type_ == 'NXST' and name.endswith('_REQUEST'):
184 if version == VERSION["1.0"]:
185 hdrs = (version, OFPT10_STATS_REQUEST, OFPST_VENDOR,
186 NX_VENDOR_ID, number)
188 hdrs = (version, OFPT11_STATS_REQUEST, OFPST_VENDOR,
189 NX_VENDOR_ID, number)
190 elif type_ == 'NXST' and name.endswith('_REPLY'):
191 if version == VERSION["1.0"]:
192 hdrs = (version, OFPT10_STATS_REPLY, OFPST_VENDOR,
193 NX_VENDOR_ID, number)
195 hdrs = (version, OFPT11_STATS_REPLY, OFPST_VENDOR,
196 NX_VENDOR_ID, number)
198 fatal("type '%s' unknown" % type_)
201 error("Duplicate message definition for %s." % str(hdrs))
202 sys.stderr.write("%s: Here is the location "
203 "of the previous definition.\n"
205 all_hdrs[hdrs] = here
206 these_hdrs.append(hdrs)
209 if contents == 'void':
213 for c in [s.strip() for s in contents.split(",")]:
215 if extra_multiple == '0':
216 extra_multiple = make_sizeof(c[:-2])
218 error("Cannot have multiple [] elements")
220 min_body_elem.append(c)
223 min_body = " + ".join([make_sizeof(s)
224 for s in min_body_elem])
226 if extra_multiple == '0':
227 error("Must specify contents (use 'void' if empty)")
230 if rawname in all_raws:
231 fatal("%s: Duplicate name" % rawname)
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,
239 "human_name": human_name,
240 "line": first_line_number}
241 all_raws_order.append(rawname)
247 if re.match('enum ofptype', line):
253 if re.match(r'\s*/?\*', line) or line.isspace():
255 elif re.match('}', line):
258 if not re.match(r'\s*OFPTYPE_.*/\*', line):
259 fatal("unexpected syntax between OFPTYPE_ definitions")
261 syntax = line.strip()
262 while not syntax.endswith('*/'):
264 if not line.strip().startswith('*'):
265 fatal("unexpected syntax within OFPTYPE_ definition")
266 syntax += ' %s' % line.strip().lstrip('* \t')
267 syntax = syntax.strip()
269 m = re.match(r'(OFPTYPE_[A-Z0-9_]+),\s*/\* (.*) \*/', syntax)
271 fatal("syntax error in OFPTYPE_ definition")
273 ofptype, raws_ = m.groups()
274 raws = [s.rstrip('.') for s in raws_.split()]
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
285 all_types.append(all_raws[raws[0]]["human_name"])
293 output.append("/* Generated automatically; do not modify! "
294 "-*- buffer-read-only: t -*- */")
297 for raw in all_raws_order:
299 output.append("static struct raw_instance %s_instances[] = {"
301 for hdrs in r['hdrs']:
302 output.append(" { {0, NULL}, {%d, %d, %d, 0x%x, %d}, %s, 0 },"
309 output.append("static struct raw_info raw_infos[] = {")
310 for raw in all_raws_order:
312 if "ofptype" not in r:
313 error("%s: no defined OFPTYPE_" % raw)
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"])
327 if r['type'].endswith("ST"):
328 for hdrs in r['hdrs']:
330 if hdrs[0] == VERSION["1.0"]:
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
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
344 if tuple(op_hdrs) not in all_hdrs:
345 if r["human_name"].endswith("request"):
346 fatal("%s has no corresponding reply"
349 fatal("%s has no corresponding request"
354 output.append("static const char *type_names[] = {");
356 output.append(" \"%s\"," % t)
365 if __name__ == '__main__':
366 if '--help' in sys.argv:
368 elif len(sys.argv) != 3:
369 sys.stderr.write("exactly two non-option arguments required; "
370 "use --help for help\n")
376 file_name = sys.argv[1]
377 input_file = open(file_name)
380 for line in extract_ofp_msgs(sys.argv[2]):