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