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