api: verify message size on receipt
[vpp.git] / src / tools / vppapigen / vppapigen_c.py
1 #
2 # Copyright (c) 2020 Cisco and/or its affiliates.
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at:
6 #
7 #     http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14 #
15
16 #
17 # Provide two classes FromJSON and TOJSON that converts between JSON and VPP's
18 # binary API format
19 #
20
21 '''
22 This module creates C code for core VPP, VPP plugins and client side VAT and
23 VAT2 tests.
24 '''
25
26 import datetime
27 import itertools
28 import os
29 import time
30 import sys
31 from io import StringIO
32 import shutil
33
34 process_imports = False
35
36
37 ###############################################################################
38 class ToJSON():
39     '''Class to generate functions converting from VPP binary API to JSON.'''
40     _dispatch = {}
41     noprint_fields = {'_vl_msg_id': None,
42                       'client_index': None,
43                       'context': None}
44     is_number = {'u8': None,
45                  'i8': None,
46                  'u16': None,
47                  'i16': None,
48                  'u32': None,
49                  'i32': None,
50                  'u64': None,
51                  'i64': None,
52                  'f64': None,
53                  }
54
55     def __init__(self, module, types, defines, imported_types, stream):
56         self.stream = stream
57         self.module = module
58         self.defines = defines
59         self.types = types
60         self.types_hash = {'vl_api_'+d.name+'_t':
61                            d for d in types + imported_types}
62         self.defines_hash = {d.name: d for d in defines}
63
64     def header(self):
65         '''Output the top boilerplate.'''
66         write = self.stream.write
67         write('#ifndef included_{}_api_tojson_h\n'.format(self.module))
68         write('#define included_{}_api_tojson_h\n'.format(self.module))
69         write('#include <vppinfra/cJSON.h>\n\n')
70         write('#include <vppinfra/jsonformat.h>\n\n')
71         if self.module == 'interface_types':
72             write('#define vl_printfun\n')
73             write('#include <vnet/interface_types.api.h>\n\n')
74
75     def footer(self):
76         '''Output the bottom boilerplate.'''
77         write = self.stream.write
78         write('#endif\n')
79
80     def get_base_type(self, t):
81         vt_type = None
82         try:
83             vt = self.types_hash[t]
84             if vt.type == 'Using' and 'length' not in vt.alias:
85                 vt_type = vt.alias['type']
86         except KeyError:
87             vt = t
88         return vt, vt_type
89
90     def get_json_func(self, t):
91         '''Given the type, returns the function to use to create a
92         cJSON object'''
93         vt, vt_type = self.get_base_type(t)
94
95         if t in self.is_number or vt_type in self.is_number:
96             return 'cJSON_AddNumberToObject', '', False
97         if t == 'bool':
98             return 'cJSON_AddBoolToObject', '', False
99
100         # Lookup type name check if it's enum
101         if vt.type == 'Enum' or vt.type == 'EnumFlag':
102             return '{t}_tojson'.format(t=t), '', True
103         return '{t}_tojson'.format(t=t), '&', True
104
105     def get_json_array_func(self, t):
106         '''Given a type returns the function to create a cJSON object
107         for arrays.'''
108         if t in self.is_number:
109             return 'cJSON_CreateNumber', ''
110         if t == 'bool':
111             return 'cJSON_CreateBool', ''
112         vt, vt_type = self.get_base_type(t)
113         if vt.type == 'Enum' or vt.type == 'EnumFlag':
114             return '{t}_tojson'.format(t=t), ''
115         return '{t}_tojson'.format(t=t), '&'
116
117     def print_string(self, o):
118         '''Create cJSON object from vl_api_string_t'''
119         write = self.stream.write
120         if o.modern_vla:
121             write('    vl_api_string_cJSON_AddToObject(o, "{n}", &a->{n});\n'
122                   .format(n=o.fieldname))
123         else:
124
125             write('    cJSON_AddStringToObject(o, "{n}", (char *)a->{n});\n'
126                   .format(n=o.fieldname))
127
128     def print_field(self, o):
129         '''Called for every field in a typedef or define.'''
130         write = self.stream.write
131         if o.fieldname in self.noprint_fields:
132             return
133
134         f, p, newobj = self.get_json_func(o.fieldtype)
135
136         if newobj:
137             write('    cJSON_AddItemToObject(o, "{n}", {f}({p}a->{n}));\n'
138                   .format(f=f, p=p, n=o.fieldname))
139         else:
140             write('    {f}(o, "{n}", {p}a->{n});\n'
141                   .format(f=f, p=p, n=o.fieldname))
142
143     _dispatch['Field'] = print_field
144
145     def print_array(self, o):
146         '''Converts a VPP API array to cJSON array.'''
147         write = self.stream.write
148
149         forloop = '''\
150     {{
151         int i;
152         cJSON *array = cJSON_AddArrayToObject(o, "{n}");
153         for (i = 0; i < {lfield}; i++) {{
154             cJSON_AddItemToArray(array, {f}({p}a->{n}[i]));
155         }}
156     }}
157 '''
158
159         if o.fieldtype == 'string':
160             self.print_string(o)
161             return
162
163         lfield = 'a->' + o.lengthfield if o.lengthfield else o.length
164         if o.fieldtype == 'u8':
165             write('    {\n')
166             # What is length field doing here?
167             write('    u8 *s = format(0, "0x%U", format_hex_bytes, '
168                   '&a->{n}, {lfield});\n'
169                   .format(n=o.fieldname, lfield=lfield))
170             write('    cJSON_AddStringToObject(o, "{n}", (char *)s);\n'
171                   .format(n=o.fieldname))
172             write('    vec_free(s);\n')
173             write('    }\n')
174             return
175
176         f, p = self.get_json_array_func(o.fieldtype)
177         write(forloop.format(lfield=lfield,
178                              t=o.fieldtype,
179                              n=o.fieldname,
180                              f=f,
181                              p=p))
182
183     _dispatch['Array'] = print_array
184
185     def print_enum(self, o):
186         '''Create cJSON object (string) for VPP API enum'''
187         write = self.stream.write
188         write('static inline cJSON *vl_api_{name}_t_tojson '
189               '(vl_api_{name}_t a) {{\n'.format(name=o.name))
190
191         write("    switch(a) {\n")
192         for b in o.block:
193             write("    case %s:\n" % b[1])
194             write('        return cJSON_CreateString("{}");\n'.format(b[0]))
195         write('    default: return cJSON_CreateString("Invalid ENUM");\n')
196         write('    }\n')
197         write('    return 0;\n')
198         write('}\n')
199
200     _dispatch['Enum'] = print_enum
201
202     def print_enum_flag(self, o):
203         '''Create cJSON object (string) for VPP API enum'''
204         write = self.stream.write
205         write('static inline cJSON *vl_api_{name}_t_tojson '
206               '(vl_api_{name}_t a) {{\n'.format(name=o.name))
207         write('    cJSON *array = cJSON_CreateArray();\n')
208
209         for b in o.block:
210             if b[1] == 0:
211                     continue
212             write('    if (a & {})\n'.format(b[0]))
213             write(
214                 '       cJSON_AddItemToArray(array, cJSON_CreateString("{}"));\n'.format(b[0]))
215         write('    return array;\n')
216         write('}\n')
217
218     _dispatch['EnumFlag'] = print_enum_flag
219
220     def print_typedef(self, o):
221         '''Create cJSON (dictionary) object from VPP API typedef'''
222         write = self.stream.write
223         write('static inline cJSON *vl_api_{name}_t_tojson '
224               '(vl_api_{name}_t *a) {{\n'.format(name=o.name))
225         write('    cJSON *o = cJSON_CreateObject();\n')
226
227         for t in o.block:
228             self._dispatch[t.type](self, t)
229
230         write('    return o;\n')
231         write('}\n')
232
233     def print_define(self, o):
234         '''Create cJSON (dictionary) object from VPP API define'''
235         write = self.stream.write
236         write('static inline cJSON *vl_api_{name}_t_tojson '
237               '(vl_api_{name}_t *a) {{\n'.format(name=o.name))
238         write('    cJSON *o = cJSON_CreateObject();\n')
239         write('    cJSON_AddStringToObject(o, "_msgname", "{}");\n'
240               .format(o.name))
241         write('    cJSON_AddStringToObject(o, "_crc", "{crc:08x}");\n'
242               .format(crc=o.crc))
243
244         for t in o.block:
245             self._dispatch[t.type](self, t)
246
247         write('    return o;\n')
248         write('}\n')
249
250     def print_using(self, o):
251         '''Create cJSON (dictionary) object from VPP API aliased type'''
252         if o.manual_print:
253             return
254
255         write = self.stream.write
256         write('static inline cJSON *vl_api_{name}_t_tojson '
257               '(vl_api_{name}_t *a) {{\n'.format(name=o.name))
258
259         write('    u8 *s = format(0, "%U", format_vl_api_{}_t, a);\n'
260               .format(o.name))
261         write('    cJSON *o = cJSON_CreateString((char *)s);\n')
262         write('    vec_free(s);\n')
263         write('    return o;\n')
264         write('}\n')
265
266     _dispatch['Typedef'] = print_typedef
267     _dispatch['Define'] = print_define
268     _dispatch['Using'] = print_using
269     _dispatch['Union'] = print_typedef
270
271     def generate_function(self, t):
272         '''Main entry point'''
273         write = self.stream.write
274         if t.manual_print:
275             write('/* Manual print {} */\n'.format(t.name))
276             return
277         self._dispatch[t.type](self, t)
278
279     def generate_types(self):
280         '''Main entry point'''
281         for t in self.types:
282             self.generate_function(t)
283
284     def generate_defines(self):
285         '''Main entry point'''
286         for t in self.defines:
287             self.generate_function(t)
288
289
290 class FromJSON():
291     '''
292     Parse JSON objects into VPP API binary message structures.
293     '''
294     _dispatch = {}
295     noprint_fields = {'_vl_msg_id': None,
296                       'client_index': None,
297                       'context': None}
298     is_number = {'u8': None,
299                  'i8': None,
300                  'u16': None,
301                  'i16': None,
302                  'u32': None,
303                  'i32': None,
304                  'u64': None,
305                  'i64': None,
306                  'f64': None,
307                  }
308
309     def __init__(self, module, types, defines, imported_types, stream):
310         self.stream = stream
311         self.module = module
312         self.defines = defines
313         self.types = types
314         self.types_hash = {'vl_api_'+d.name+'_t':
315                            d for d in types + imported_types}
316         self.defines_hash = {d.name: d for d in defines}
317
318     def header(self):
319         '''Output the top boilerplate.'''
320         write = self.stream.write
321         write('#ifndef included_{}_api_fromjson_h\n'.format(self.module))
322         write('#define included_{}_api_fromjson_h\n'.format(self.module))
323         write('#include <vppinfra/cJSON.h>\n\n')
324         write('#include <vppinfra/jsonformat.h>\n\n')
325         write('#pragma GCC diagnostic ignored "-Wunused-label"\n')
326
327     def is_base_type(self, t):
328         '''Check if a type is one of the VPP API base types'''
329         if t in self.is_number:
330             return True
331         if t == 'bool':
332             return True
333         return False
334
335     def footer(self):
336         '''Output the bottom boilerplate.'''
337         write = self.stream.write
338         write('#endif\n')
339
340     def print_string(self, o, toplevel=False):
341         '''Convert JSON string to vl_api_string_t'''
342         write = self.stream.write
343
344         msgvar = "a" if toplevel else "*mp"
345         msgsize = "l" if toplevel else "*len"
346
347         if o.modern_vla:
348             write('    char *p = cJSON_GetStringValue(item);\n')
349             write('    size_t plen = strlen(p);\n')
350             write('    {msgvar} = cJSON_realloc({msgvar}, {msgsize} + plen, {msgsize});\n'
351                   .format(msgvar=msgvar, msgsize=msgsize))
352             write('    if ({msgvar} == 0) goto error;\n'.format(msgvar=msgvar))
353             write('    vl_api_c_string_to_api_string(p, (void *){msgvar} + '
354                   '{msgsize} - sizeof(vl_api_string_t));\n'
355                   .format(msgvar=msgvar, msgsize=msgsize))
356             write('    {msgsize} += plen;\n'.format(msgsize=msgsize))
357         else:
358             write('    strncpy_s((char *)a->{n}, sizeof(a->{n}), '
359                   'cJSON_GetStringValue(item), sizeof(a->{n}) - 1);\n'
360                   .format(n=o.fieldname))
361
362     def print_field(self, o, toplevel=False):
363         '''Called for every field in a typedef or define.'''
364         write = self.stream.write
365         if o.fieldname in self.noprint_fields:
366             return
367         is_bt = self.is_base_type(o.fieldtype)
368         t = 'vl_api_{}'.format(o.fieldtype) if is_bt else o.fieldtype
369
370         msgvar = "(void **)&a" if toplevel else "mp"
371         msgsize = "&l" if toplevel else "len"
372
373         if is_bt:
374             write('    vl_api_{t}_fromjson(item, &a->{n});\n'
375                   .format(t=o.fieldtype, n=o.fieldname))
376         else:
377             write('    if ({t}_fromjson({msgvar}, '
378                   '{msgsize}, item, &a->{n}) < 0) goto error;\n'
379                   .format(t=t, n=o.fieldname, msgvar=msgvar, msgsize=msgsize))
380
381     _dispatch['Field'] = print_field
382
383     def print_array(self, o, toplevel=False):
384         '''Convert JSON array to VPP API array'''
385         write = self.stream.write
386
387         forloop = '''\
388     {{
389         int i;
390         cJSON *array = cJSON_GetObjectItem(o, "{n}");
391         int size = cJSON_GetArraySize(array);
392         if (size != {lfield}) goto error;
393         for (i = 0; i < size; i++) {{
394             cJSON *e = cJSON_GetArrayItem(array, i);
395             {call}
396         }}
397     }}
398 '''
399         forloop_vla = '''\
400     {{
401         int i;
402         cJSON *array = cJSON_GetObjectItem(o, "{n}");
403         int size = cJSON_GetArraySize(array);
404         {lfield} = size;
405         {realloc} = cJSON_realloc({realloc}, {msgsize} + sizeof({t}) * size, {msgsize});
406         {t} *d = (void *){realloc} + {msgsize};
407         {msgsize} += sizeof({t}) * size;
408         for (i = 0; i < size; i++) {{
409             cJSON *e = cJSON_GetArrayItem(array, i);
410             {call}
411         }}
412     }}
413 '''
414         t = o.fieldtype
415         if o.fieldtype == 'string':
416             self.print_string(o, toplevel)
417             return
418
419         lfield = 'a->' + o.lengthfield if o.lengthfield else o.length
420         msgvar = "(void **)&a" if toplevel else "mp"
421         realloc = "a" if toplevel else "*mp"
422         msgsize = "l" if toplevel else "*len"
423
424         if o.fieldtype == 'u8':
425             if o.lengthfield:
426                 write('    s = u8string_fromjson(o, "{}");\n'
427                       .format(o.fieldname))
428                 write('    if (!s) goto error;\n')
429                 write('    {} = vec_len(s);\n'.format(lfield))
430
431                 write('    {realloc} = cJSON_realloc({realloc}, {msgsize} + '
432                       'vec_len(s), {msgsize});\n'.format(msgvar=msgvar, msgsize=msgsize, realloc=realloc))
433                 write('    memcpy((void *){realloc} + {msgsize}, s, '
434                       'vec_len(s));\n'.format(realloc=realloc, msgsize=msgsize))
435                 write('    {msgsize} += vec_len(s);\n'.format(msgsize=msgsize))
436
437                 write('    vec_free(s);\n')
438             else:
439                 write('    if (u8string_fromjson2(o, "{n}", a->{n}) < 0) goto error;\n'
440                       .format(n=o.fieldname))
441             return
442
443         is_bt = self.is_base_type(o.fieldtype)
444
445         if o.lengthfield:
446             if is_bt:
447                 call = ('vl_api_{t}_fromjson(e, &d[i]);'
448                         .format(t=o.fieldtype))
449             else:
450                 call = ('if ({t}_fromjson({msgvar}, len, e, &d[i]) < 0) goto error; '
451                         .format(t=o.fieldtype, msgvar=msgvar))
452             write(forloop_vla.format(lfield=lfield,
453                                      t=o.fieldtype,
454                                      n=o.fieldname,
455                                      call=call,
456                                      realloc=realloc,
457                                      msgsize=msgsize))
458         else:
459             if is_bt:
460                 call = ('vl_api_{t}_fromjson(e, &a->{n}[i]);'
461                         .format(t=t, n=o.fieldname))
462             else:
463                 call = ('if ({}_fromjson({}, len, e, &a->{}[i]) < 0) goto error;'
464                         .format(t, msgvar, o.fieldname))
465             write(forloop.format(lfield=lfield,
466                                  t=t,
467                                  n=o.fieldname,
468                                  call=call,
469                                  msgvar=msgvar,
470                                  realloc=realloc,
471                                  msgsize=msgsize))
472
473     _dispatch['Array'] = print_array
474
475     def print_enum(self, o):
476         '''Convert to JSON enum(string) to VPP API enum (int)'''
477         write = self.stream.write
478         write('static inline int vl_api_{n}_t_fromjson'
479               '(void **mp, int *len, cJSON *o, vl_api_{n}_t *a) {{\n'
480               .format(n=o.name))
481         write('    char *p = cJSON_GetStringValue(o);\n')
482         for b in o.block:
483             write('    if (strcmp(p, "{}") == 0) {{*a = {}; return 0;}}\n'
484                   .format(b[0], b[1]))
485         write('    *a = 0;\n')
486         write('    return -1;\n')
487         write('}\n')
488
489     _dispatch['Enum'] = print_enum
490
491     def print_enum_flag(self, o):
492         '''Convert to JSON enum(string) to VPP API enum (int)'''
493         write = self.stream.write
494         write('static inline int vl_api_{n}_t_fromjson '
495               '(void **mp, int *len, cJSON *o, vl_api_{n}_t *a) {{\n'
496               .format(n=o.name))
497         write('   int i;\n')
498         write('   *a = 0;\n')
499         write('   for (i = 0; i < cJSON_GetArraySize(o); i++) {\n')
500         write('       cJSON *e = cJSON_GetArrayItem(o, i);\n')
501         write('       char *p = cJSON_GetStringValue(e);\n')
502         write('       if (!p) return -1;\n')
503         for b in o.block:
504             write('       if (strcmp(p, "{}") == 0) *a |= {};\n'
505                   .format(b[0], b[1]))
506         write('    }\n')
507         write('   return 0;\n')
508         write('}\n')
509
510     _dispatch['EnumFlag'] = print_enum_flag
511
512     def print_typedef(self, o):
513         '''Convert from JSON object to VPP API binary representation'''
514         write = self.stream.write
515
516         write('static inline int vl_api_{name}_t_fromjson (void **mp, '
517               'int *len, cJSON *o, vl_api_{name}_t *a) {{\n'
518               .format(name=o.name))
519         write('    cJSON *item __attribute__ ((unused));\n')
520         write('    u8 *s __attribute__ ((unused));\n')
521         for t in o.block:
522             if t.type == 'Field' and t.is_lengthfield:
523                 continue
524             write('\n    item = cJSON_GetObjectItem(o, "{}");\n'
525                   .format(t.fieldname))
526             write('    if (!item) goto error;\n')
527             self._dispatch[t.type](self, t)
528
529         write('\n    return 0;\n')
530         write('\n  error:\n')
531         write('    return -1;\n')
532         write('}\n')
533
534     def print_union(self, o):
535         '''Convert JSON object to VPP API binary union'''
536         write = self.stream.write
537
538         write('static inline int vl_api_{name}_t_fromjson (void **mp, '
539               'int *len, cJSON *o, vl_api_{name}_t *a) {{\n'
540               .format(name=o.name))
541         write('    cJSON *item __attribute__ ((unused));\n')
542         write('    u8 *s __attribute__ ((unused));\n')
543         for t in o.block:
544             if t.type == 'Field' and t.is_lengthfield:
545                 continue
546             write('    item = cJSON_GetObjectItem(o, "{}");\n'
547                   .format(t.fieldname))
548             write('    if (item) {\n')
549             self._dispatch[t.type](self, t)
550             write('    };\n')
551         write('\n    return 0;\n')
552         write('\n  error:\n')
553         write('    return -1;\n')
554         write('}\n')
555
556     def print_define(self, o):
557         '''Convert JSON object to VPP API message'''
558         write = self.stream.write
559         error = 0
560         write('static inline vl_api_{name}_t *vl_api_{name}_t_fromjson '
561               '(cJSON *o, int *len) {{\n'.format(name=o.name))
562         write('    cJSON *item __attribute__ ((unused));\n')
563         write('    u8 *s __attribute__ ((unused));\n')
564         write('    int l = sizeof(vl_api_{}_t);\n'.format(o.name))
565         write('    vl_api_{}_t *a = cJSON_malloc(l);\n'.format(o.name))
566         write('\n')
567
568         for t in o.block:
569             if t.fieldname in self.noprint_fields:
570                 continue
571             if t.type == 'Field' and t.is_lengthfield:
572                 continue
573             write('    item = cJSON_GetObjectItem(o, "{}");\n'
574                   .format(t.fieldname))
575             write('    if (!item) goto error;\n')
576             error += 1
577             self._dispatch[t.type](self, t, toplevel=True)
578             write('\n')
579
580         write('    *len = l;\n')
581         write('    return a;\n')
582
583         if error:
584             write('\n  error:\n')
585             write('    cJSON_free(a);\n')
586             write('    return 0;\n')
587         write('}\n')
588
589     def print_using(self, o):
590         '''Convert JSON field to VPP type alias'''
591         write = self.stream.write
592
593         if o.manual_print:
594             return
595
596         t = o.using
597         write('static inline int vl_api_{name}_t_fromjson (void **mp, '
598               'int *len, cJSON *o, vl_api_{name}_t *a) {{\n'
599               .format(name=o.name))
600         if 'length' in o.alias:
601             if t.fieldtype != 'u8':
602                 raise ValueError("Error in processing type {} for {}"
603                                  .format(t.fieldtype, o.name))
604             write('    vl_api_u8_string_fromjson(o, (u8 *)a, {});\n'
605                   .format(o.alias['length']))
606         else:
607             write('    vl_api_{t}_fromjson(o, ({t} *)a);\n'
608                   .format(t=t.fieldtype))
609
610         write('    return 0;\n')
611         write('}\n')
612
613     _dispatch['Typedef'] = print_typedef
614     _dispatch['Define'] = print_define
615     _dispatch['Using'] = print_using
616     _dispatch['Union'] = print_union
617
618     def generate_function(self, t):
619         '''Main entry point'''
620         write = self.stream.write
621         if t.manual_print:
622             write('/* Manual print {} */\n'.format(t.name))
623             return
624         self._dispatch[t.type](self, t)
625
626     def generate_types(self):
627         '''Main entry point'''
628         for t in self.types:
629             self.generate_function(t)
630
631     def generate_defines(self):
632         '''Main entry point'''
633         for t in self.defines:
634             self.generate_function(t)
635
636
637 def generate_tojson(s, modulename, stream):
638     '''Generate all functions to convert from API to JSON'''
639     write = stream.write
640
641     write('/* Imported API files */\n')
642     for i in s['Import']:
643         f = i.filename.replace('plugins/', '')
644         write('#include <{}_tojson.h>\n'.format(f))
645
646     pp = ToJSON(modulename, s['types'], s['Define'], s['imported']['types'],
647                 stream)
648     pp.header()
649     pp.generate_types()
650     pp.generate_defines()
651     pp.footer()
652     return ''
653
654
655 def generate_fromjson(s, modulename, stream):
656     '''Generate all functions to convert from JSON to API'''
657     write = stream.write
658     write('/* Imported API files */\n')
659     for i in s['Import']:
660         f = i.filename.replace('plugins/', '')
661         write('#include <{}_fromjson.h>\n'.format(f))
662
663     pp = FromJSON(modulename, s['types'], s['Define'], s['imported']['types'],
664                   stream)
665     pp.header()
666     pp.generate_types()
667     pp.generate_defines()
668     pp.footer()
669
670     return ''
671
672 ###############################################################################
673
674
675 DATESTRING = datetime.datetime.utcfromtimestamp(
676     int(os.environ.get('SOURCE_DATE_EPOCH', time.time())))
677 TOP_BOILERPLATE = '''\
678 /*
679  * VLIB API definitions {datestring}
680  * Input file: {input_filename}
681  * Automatically generated: please edit the input file NOT this file!
682  */
683
684 #include <stdbool.h>
685 #if defined(vl_msg_id)||defined(vl_union_id) \\
686     || defined(vl_printfun) ||defined(vl_endianfun) \\
687     || defined(vl_api_version)||defined(vl_typedefs) \\
688     || defined(vl_msg_name)||defined(vl_msg_name_crc_list) \\
689     || defined(vl_api_version_tuple) || defined(vl_calcsizefun)
690 /* ok, something was selected */
691 #else
692 #warning no content included from {input_filename}
693 #endif
694
695 #define VL_API_PACKED(x) x __attribute__ ((packed))
696 '''
697
698 BOTTOM_BOILERPLATE = '''\
699 /****** API CRC (whole file) *****/
700
701 #ifdef vl_api_version
702 vl_api_version({input_filename}, {file_crc:#08x})
703
704 #endif
705 '''
706
707
708 def msg_ids(s):
709     '''Generate macro to map API message id to handler'''
710     output = '''\
711
712 /****** Message ID / handler enum ******/
713
714 #ifdef vl_msg_id
715 '''
716
717     for t in s['Define']:
718         output += "vl_msg_id(VL_API_%s, vl_api_%s_t_handler)\n" % \
719                   (t.name.upper(), t.name)
720     output += "#endif"
721
722     return output
723
724
725 def msg_names(s):
726     '''Generate calls to name mapping macro'''
727     output = '''\
728
729 /****** Message names ******/
730
731 #ifdef vl_msg_name
732 '''
733
734     for t in s['Define']:
735         dont_trace = 0 if t.dont_trace else 1
736         output += "vl_msg_name(vl_api_%s_t, %d)\n" % (t.name, dont_trace)
737     output += "#endif"
738
739     return output
740
741
742 def msg_name_crc_list(s, suffix):
743     '''Generate list of names to CRC mappings'''
744     output = '''\
745
746 /****** Message name, crc list ******/
747
748 #ifdef vl_msg_name_crc_list
749 '''
750     output += "#define foreach_vl_msg_name_crc_%s " % suffix
751
752     for t in s['Define']:
753         output += "\\\n_(VL_API_%s, %s, %08x) " % \
754             (t.name.upper(), t.name, t.crc)
755     output += "\n#endif"
756
757     return output
758
759
760 def api2c(fieldtype):
761     '''Map between API type names and internal VPP type names'''
762     mappingtable = {'string': 'vl_api_string_t', }
763     if fieldtype in mappingtable:
764         return mappingtable[fieldtype]
765     return fieldtype
766
767
768 def typedefs(filename):
769     '''Include in the main files to the types file'''
770     output = '''\
771
772 /****** Typedefs ******/
773
774 #ifdef vl_typedefs
775 #include "{include}.api_types.h"
776 #endif
777 '''.format(include=filename)
778     return output
779
780
781 FORMAT_STRINGS = {'u8': '%u',
782                   'bool': '%u',
783                   'i8': '%d',
784                   'u16': '%u',
785                   'i16': '%d',
786                   'u32': '%u',
787                   'i32': '%ld',
788                   'u64': '%llu',
789                   'i64': '%lld',
790                   'f64': '%.2f'}
791
792
793 class Printfun():
794     '''Functions for pretty printing VPP API messages'''
795     _dispatch = {}
796     noprint_fields = {'_vl_msg_id': None,
797                       'client_index': None,
798                       'context': None}
799
800     def __init__(self, stream):
801         self.stream = stream
802
803     @staticmethod
804     def print_string(o, stream):
805         '''Pretty print a vl_api_string_t'''
806         write = stream.write
807         if o.modern_vla:
808             write('    if (vl_api_string_len(&a->{f}) > 0) {{\n'
809                   .format(f=o.fieldname))
810             write('        s = format(s, "\\n%U{f}: %U", '
811                   'format_white_space, indent, '
812                   'vl_api_format_string, (&a->{f}));\n'.format(f=o.fieldname))
813             write('    } else {\n')
814             write('        s = format(s, "\\n%U{f}:", '
815                   'format_white_space, indent);\n'.format(f=o.fieldname))
816             write('    }\n')
817         else:
818             write('    s = format(s, "\\n%U{f}: %s", '
819                   'format_white_space, indent, a->{f});\n'
820                   .format(f=o.fieldname))
821
822     def print_field(self, o, stream):
823         '''Pretty print API field'''
824         write = stream.write
825         if o.fieldname in self.noprint_fields:
826             return
827         if o.fieldtype in FORMAT_STRINGS:
828             f = FORMAT_STRINGS[o.fieldtype]
829             write('    s = format(s, "\\n%U{n}: {f}", '
830                   'format_white_space, indent, a->{n});\n'
831                   .format(n=o.fieldname, f=f))
832         else:
833             write('    s = format(s, "\\n%U{n}: %U", '
834                   'format_white_space, indent, '
835                   'format_{t}, &a->{n}, indent);\n'
836                   .format(n=o.fieldname, t=o.fieldtype))
837
838     _dispatch['Field'] = print_field
839
840     def print_array(self, o, stream):
841         '''Pretty print API array'''
842         write = stream.write
843
844         forloop = '''\
845     for (i = 0; i < {lfield}; i++) {{
846         s = format(s, "\\n%U{n}: %U",
847                    format_white_space, indent, format_{t}, &a->{n}[i], indent);
848     }}
849 '''
850
851         forloop_format = '''\
852     for (i = 0; i < {lfield}; i++) {{
853         s = format(s, "\\n%U{n}: {t}",
854                    format_white_space, indent, a->{n}[i]);
855     }}
856 '''
857
858         if o.fieldtype == 'string':
859             self.print_string(o, stream)
860             return
861
862         if o.fieldtype == 'u8':
863             if o.lengthfield:
864                 write('    s = format(s, "\\n%U{n}: %U", format_white_space, '
865                       'indent, format_hex_bytes, a->{n}, a->{lfield});\n'
866                       .format(n=o.fieldname, lfield=o.lengthfield))
867             else:
868                 write('    s = format(s, "\\n%U{n}: %U", format_white_space, '
869                       'indent, format_hex_bytes, a, {lfield});\n'
870                       .format(n=o.fieldname, lfield=o.length))
871             return
872
873         lfield = 'a->' + o.lengthfield if o.lengthfield else o.length
874         if o.fieldtype in FORMAT_STRINGS:
875             write(forloop_format.format(lfield=lfield,
876                                         t=FORMAT_STRINGS[o.fieldtype],
877                                         n=o.fieldname))
878         else:
879             write(forloop.format(lfield=lfield, t=o.fieldtype, n=o.fieldname))
880
881     _dispatch['Array'] = print_array
882
883     @staticmethod
884     def print_alias(k, v, stream):
885         '''Pretty print type alias'''
886         write = stream.write
887         if ('length' in v.alias and v.alias['length'] and
888                 v.alias['type'] == 'u8'):
889             write('    return format(s, "%U", format_hex_bytes, a, {});\n'
890                   .format(v.alias['length']))
891         elif v.alias['type'] in FORMAT_STRINGS:
892             write('    return format(s, "{}", *a);\n'
893                   .format(FORMAT_STRINGS[v.alias['type']]))
894         else:
895             write('    return format(s, "{} (print not implemented)");\n'
896                   .format(k))
897
898     @staticmethod
899     def print_enum(o, stream):
900         '''Pretty print API enum'''
901         write = stream.write
902         write("    switch(*a) {\n")
903         for b in o:
904             write("    case %s:\n" % b[1])
905             write('        return format(s, "{}");\n'.format(b[0]))
906         write('    }\n')
907
908     _dispatch['Enum'] = print_enum
909     _dispatch['EnumFlag'] = print_enum
910
911     def print_obj(self, o, stream):
912         '''Entry point'''
913         write = stream.write
914
915         if o.type in self._dispatch:
916             self._dispatch[o.type](self, o, stream)
917         else:
918             write('    s = format(s, "\\n{} {} {} (print not implemented");\n'
919                   .format(o.type, o.fieldtype, o.fieldname))
920
921
922 def printfun(objs, stream, modulename):
923     '''Main entry point for pretty print function generation'''
924     write = stream.write
925
926     h = '''\
927 /****** Print functions *****/
928 #ifdef vl_printfun
929 #ifndef included_{module}_printfun
930 #define included_{module}_printfun
931
932 #ifdef LP64
933 #define _uword_fmt \"%lld\"
934 #define _uword_cast (long long)
935 #else
936 #define _uword_fmt \"%ld\"
937 #define _uword_cast long
938 #endif
939
940 #include "{module}.api_tojson.h"
941 #include "{module}.api_fromjson.h"
942
943 '''
944
945     signature = '''\
946 static inline void *vl_api_{name}_t_print{suffix} (vl_api_{name}_t *a, void *handle)
947 {{
948     u8 *s = 0;
949     u32 indent __attribute__((unused)) = 2;
950     int i __attribute__((unused));
951 '''
952
953     h = h.format(module=modulename)
954     write(h)
955
956     pp = Printfun(stream)
957     for t in objs:
958         if t.manual_print:
959             write("/***** manual: vl_api_%s_t_print  *****/\n\n" % t.name)
960             continue
961         write(signature.format(name=t.name, suffix=''))
962         write('    /* Message definition: vl_api_{}_t: */\n'.format(t.name))
963         write("    s = format(s, \"vl_api_%s_t:\");\n" % t.name)
964         for o in t.block:
965             pp.print_obj(o, stream)
966         write('    vec_add1(s, 0);\n')
967         write('    vl_print (handle, (char *)s);\n')
968         write('    vec_free (s);\n')
969         write('    return handle;\n')
970         write('}\n\n')
971
972         write(signature.format(name=t.name, suffix='_json'))
973         write('    cJSON * o = vl_api_{}_t_tojson(a);\n'.format(t.name))
974         write('    (void)s;\n')
975         write('    char *out = cJSON_Print(o);\n')
976         write('    vl_print(handle, out);\n')
977         write('    cJSON_Delete(o);\n')
978         write('    cJSON_free(out);\n')
979         write('    return handle;\n')
980         write('}\n\n')
981
982     write("\n#endif")
983     write("\n#endif /* vl_printfun */\n")
984
985     return ''
986
987
988 def printfun_types(objs, stream, modulename):
989     '''Pretty print API types'''
990     write = stream.write
991     pp = Printfun(stream)
992
993     h = '''\
994 /****** Print functions *****/
995 #ifdef vl_printfun
996 #ifndef included_{module}_printfun_types
997 #define included_{module}_printfun_types
998
999 '''
1000     h = h.format(module=modulename)
1001     write(h)
1002
1003     signature = '''\
1004 static inline u8 *format_vl_api_{name}_t (u8 *s, va_list * args)
1005 {{
1006     vl_api_{name}_t *a = va_arg (*args, vl_api_{name}_t *);
1007     u32 indent __attribute__((unused)) = va_arg (*args, u32);
1008     int i __attribute__((unused));
1009     indent += 2;
1010 '''
1011
1012     for t in objs:
1013         if t.__class__.__name__ == 'Enum' or t.__class__.__name__ == 'EnumFlag':
1014             write(signature.format(name=t.name))
1015             pp.print_enum(t.block, stream)
1016             write('    return s;\n')
1017             write('}\n\n')
1018             continue
1019
1020         if t.manual_print:
1021             write("/***** manual: vl_api_%s_t_print  *****/\n\n" % t.name)
1022             continue
1023
1024         if t.__class__.__name__ == 'Using':
1025             write(signature.format(name=t.name))
1026             pp.print_alias(t.name, t, stream)
1027             write('}\n\n')
1028             continue
1029
1030         write(signature.format(name=t.name))
1031         for o in t.block:
1032             pp.print_obj(o, stream)
1033
1034         write('    return s;\n')
1035         write('}\n\n')
1036
1037     write("\n#endif")
1038     write("\n#endif /* vl_printfun_types */\n")
1039
1040
1041 def generate_imports(imports):
1042     '''Add #include matching the API import statements'''
1043     output = '/* Imported API files */\n'
1044     output += '#ifndef vl_api_version\n'
1045
1046     for i in imports:
1047         s = i.filename.replace('plugins/', '')
1048         output += '#include <{}.h>\n'.format(s)
1049     output += '#endif\n'
1050     return output
1051
1052
1053 ENDIAN_STRINGS = {
1054     'u16': 'clib_net_to_host_u16',
1055     'u32': 'clib_net_to_host_u32',
1056     'u64': 'clib_net_to_host_u64',
1057     'i16': 'clib_net_to_host_i16',
1058     'i32': 'clib_net_to_host_i32',
1059     'i64': 'clib_net_to_host_i64',
1060     'f64': 'clib_net_to_host_f64',
1061 }
1062
1063
1064 def endianfun_array(o):
1065     '''Generate endian functions for arrays'''
1066     forloop = '''\
1067     for (i = 0; i < {length}; i++) {{
1068         a->{name}[i] = {format}(a->{name}[i]);
1069     }}
1070 '''
1071
1072     forloop_format = '''\
1073     for (i = 0; i < {length}; i++) {{
1074         {type}_endian(&a->{name}[i]);
1075     }}
1076 '''
1077
1078     output = ''
1079     if o.fieldtype == 'u8' or o.fieldtype == 'string' or o.fieldtype == 'bool':
1080         output += '    /* a->{n} = a->{n} (no-op) */\n'.format(n=o.fieldname)
1081     else:
1082         lfield = 'a->' + o.lengthfield if o.lengthfield else o.length
1083         if o.fieldtype in ENDIAN_STRINGS:
1084             output += (forloop
1085                        .format(length=lfield,
1086                                format=ENDIAN_STRINGS[o.fieldtype],
1087                                name=o.fieldname))
1088         else:
1089             output += (forloop_format
1090                        .format(length=lfield, type=o.fieldtype,
1091                                name=o.fieldname))
1092     return output
1093
1094
1095 NO_ENDIAN_CONVERSION = {'client_index': None}
1096
1097
1098 def endianfun_obj(o):
1099     '''Generate endian conversion function for type'''
1100     output = ''
1101     if o.type == 'Array':
1102         return endianfun_array(o)
1103     if o.type != 'Field':
1104         output += ('    s = format(s, "\\n{} {} {} (print not implemented");\n'
1105                    .format(o.type, o.fieldtype, o.fieldname))
1106         return output
1107     if o.fieldname in NO_ENDIAN_CONVERSION:
1108         output += '    /* a->{n} = a->{n} (no-op) */\n'.format(n=o.fieldname)
1109         return output
1110     if o.fieldtype in ENDIAN_STRINGS:
1111         output += ('    a->{name} = {format}(a->{name});\n'
1112                    .format(name=o.fieldname,
1113                            format=ENDIAN_STRINGS[o.fieldtype]))
1114     elif o.fieldtype.startswith('vl_api_'):
1115         output += ('    {type}_endian(&a->{name});\n'
1116                    .format(type=o.fieldtype, name=o.fieldname))
1117     else:
1118         output += '    /* a->{n} = a->{n} (no-op) */\n'.format(n=o.fieldname)
1119
1120     return output
1121
1122
1123 def endianfun(objs, modulename):
1124     '''Main entry point for endian function generation'''
1125     output = '''\
1126
1127 /****** Endian swap functions *****/\n\
1128 #ifdef vl_endianfun
1129 #ifndef included_{module}_endianfun
1130 #define included_{module}_endianfun
1131
1132 #undef clib_net_to_host_uword
1133 #ifdef LP64
1134 #define clib_net_to_host_uword clib_net_to_host_u64
1135 #else
1136 #define clib_net_to_host_uword clib_net_to_host_u32
1137 #endif
1138
1139 '''
1140     output = output.format(module=modulename)
1141
1142     signature = '''\
1143 static inline void vl_api_{name}_t_endian (vl_api_{name}_t *a)
1144 {{
1145     int i __attribute__((unused));
1146 '''
1147
1148     for t in objs:
1149         if t.__class__.__name__ == 'Enum' or t.__class__.__name__ == 'EnumFlag':
1150             output += signature.format(name=t.name)
1151             if t.enumtype in ENDIAN_STRINGS:
1152                 output += ('    *a = {}(*a);\n'
1153                            .format(ENDIAN_STRINGS[t.enumtype]))
1154             else:
1155                 output += ('    /* a->{name} = a->{name} (no-op) */\n'
1156                            .format(name=t.name))
1157
1158             output += '}\n\n'
1159             continue
1160
1161         if t.manual_endian:
1162             output += "/***** manual: vl_api_%s_t_endian  *****/\n\n" % t.name
1163             continue
1164
1165         if t.__class__.__name__ == 'Using':
1166             output += signature.format(name=t.name)
1167             if ('length' in t.alias and t.alias['length'] and
1168                     t.alias['type'] == 'u8'):
1169                 output += ('    /* a->{name} = a->{name} (no-op) */\n'
1170                            .format(name=t.name))
1171             elif t.alias['type'] in FORMAT_STRINGS:
1172                 output += ('    *a = {}(*a);\n'
1173                            .format(ENDIAN_STRINGS[t.alias['type']]))
1174             else:
1175                 output += '    /* Not Implemented yet {} */'.format(t.name)
1176             output += '}\n\n'
1177             continue
1178
1179         output += signature.format(name=t.name)
1180
1181         for o in t.block:
1182             output += endianfun_obj(o)
1183         output += '}\n\n'
1184
1185     output += "\n#endif"
1186     output += "\n#endif /* vl_endianfun */\n\n"
1187
1188     return output
1189
1190
1191 def calc_size_fun(objs, modulename):
1192     '''Main entry point for calculate size function generation'''
1193     output = '''\
1194
1195 /****** Calculate size functions *****/\n\
1196 #ifdef vl_calcsizefun
1197 #ifndef included_{module}_calcsizefun
1198 #define included_{module}_calcsizefun
1199
1200 '''
1201     output = output.format(module=modulename)
1202
1203     signature = '''\
1204 /* calculate message size of message in network byte order */
1205 static inline uword vl_api_{name}_t_calc_size (vl_api_{name}_t *a)
1206 {{
1207 '''
1208
1209     for o in objs:
1210         tname = o.__class__.__name__
1211
1212         output += signature.format(name=o.name)
1213         output += f"      return sizeof(*a)"
1214         if tname == 'Using':
1215             if 'length' in o.alias:
1216                 try:
1217                     tmp = int(o.alias['length'])
1218                     if tmp == 0:
1219                         raise (f"Unexpected length '0' for alias {o}")
1220                 except:
1221                     # output += f" + vl_api_{o.alias.name}_t_calc_size({o.name})"
1222                     print("culprit:")
1223                     print(o)
1224                     print(dir(o.alias))
1225                     print(o.alias)
1226                     raise
1227         elif tname == 'Enum' or tname == 'EnumFlag':
1228             pass
1229         else:
1230             for b in o.block:
1231                 if b.type == 'Option':
1232                     continue
1233                 elif b.type == 'Field':
1234                     if b.fieldtype.startswith('vl_api_'):
1235                         output += f" - sizeof(a->{b.fieldname})"
1236                         output += f" + {b.fieldtype}_calc_size(&a->{b.fieldname})"
1237                 elif b.type == 'Array':
1238                     if b.lengthfield:
1239                         m = list(filter(lambda x: x.fieldname == b.lengthfield, o.block))
1240                         if len(m) != 1:
1241                             raise Exception(f"Expected 1 match for field '{b.lengthfield}', got '{m}'")
1242                         lf = m[0]
1243                         if lf.fieldtype in ENDIAN_STRINGS:
1244                             output += f" + {ENDIAN_STRINGS[lf.fieldtype]}(a->{b.lengthfield}) * sizeof(a->{b.fieldname}[0])"
1245                         elif lf.fieldtype == "u8":
1246                             output += f" + a->{b.lengthfield} * sizeof(a->{b.fieldname}[0])"
1247                         else:
1248                             raise Exception(f"Don't know how to endian swap {lf.fieldtype}")
1249                     else:
1250                         # Fixed length strings decay to nul terminated u8
1251                         if b.fieldtype == 'string':
1252                             if b.modern_vla:
1253                                 output += f" + vl_api_string_len(&a->{b.fieldname})"
1254
1255         output += ";\n"
1256         output += '}\n\n'
1257     output += "\n#endif"
1258     output += "\n#endif /* vl_calcsizefun */\n\n"
1259
1260     return output
1261
1262
1263 def version_tuple(s, module):
1264     '''Generate semantic version string'''
1265     output = '''\
1266 /****** Version tuple *****/
1267
1268 #ifdef vl_api_version_tuple
1269
1270 '''
1271     if 'version' in s['Option']:
1272         v = s['Option']['version']
1273         (major, minor, patch) = v.split('.')
1274         output += "vl_api_version_tuple(%s, %s, %s, %s)\n" % \
1275                   (module, major, minor, patch)
1276
1277     output += "\n#endif /* vl_api_version_tuple */\n\n"
1278
1279     return output
1280
1281
1282 def generate_include_enum(s, module, stream):
1283     '''Generate <name>.api_enum.h'''
1284     write = stream.write
1285
1286     if 'Define' in s:
1287         write('typedef enum {\n')
1288         for t in s['Define']:
1289             write('   VL_API_{},\n'.format(t.name.upper()))
1290         write('   VL_MSG_{}_LAST\n'.format(module.upper()))
1291         write('}} vl_api_{}_enum_t;\n'.format(module))
1292
1293
1294 def generate_include_counters(s, stream):
1295     '''Include file for the counter data model types.'''
1296     write = stream.write
1297
1298     for counters in s:
1299         csetname = counters.name
1300         write('typedef enum {\n')
1301         for c in counters.block:
1302             write('   {}_ERROR_{},\n'
1303                   .format(csetname.upper(), c['name'].upper()))
1304         write('   {}_N_ERROR\n'.format(csetname.upper()))
1305         write('}} vl_counter_{}_enum_t;\n'.format(csetname))
1306
1307         write('extern vlib_error_desc_t {}_error_counters[];\n'.format(csetname))
1308
1309
1310 def generate_include_types(s, module, stream):
1311     '''Generate separate API _types file.'''
1312     write = stream.write
1313
1314     write('#ifndef included_{module}_api_types_h\n'.format(module=module))
1315     write('#define included_{module}_api_types_h\n'.format(module=module))
1316
1317     if 'version' in s['Option']:
1318         v = s['Option']['version']
1319         (major, minor, patch) = v.split('.')
1320         write('#define VL_API_{m}_API_VERSION_MAJOR {v}\n'
1321               .format(m=module.upper(), v=major))
1322         write('#define VL_API_{m}_API_VERSION_MINOR {v}\n'
1323               .format(m=module.upper(), v=minor))
1324         write('#define VL_API_{m}_API_VERSION_PATCH {v}\n'
1325               .format(m=module.upper(), v=patch))
1326
1327     if 'Import' in s:
1328         write('/* Imported API files */\n')
1329         for i in s['Import']:
1330             filename = i.filename.replace('plugins/', '')
1331             write('#include <{}_types.h>\n'.format(filename))
1332
1333     for o in itertools.chain(s['types'], s['Define']):
1334         tname = o.__class__.__name__
1335         if tname == 'Using':
1336             if 'length' in o.alias:
1337                 write('typedef %s vl_api_%s_t[%s];\n' %
1338                       (o.alias['type'], o.name, o.alias['length']))
1339             else:
1340                 write('typedef %s vl_api_%s_t;\n' % (o.alias['type'], o.name))
1341         elif tname == 'Enum' or tname == 'EnumFlag':
1342             if o.enumtype == 'u32':
1343                 write("typedef enum {\n")
1344             else:
1345                 write("typedef enum __attribute__((packed)) {\n")
1346
1347             for b in o.block:
1348                 write("    %s = %s,\n" % (b[0], b[1]))
1349             write('} vl_api_%s_t;\n' % o.name)
1350             if o.enumtype != 'u32':
1351                 size1 = 'sizeof(vl_api_%s_t)' % o.name
1352                 size2 = 'sizeof(%s)' % o.enumtype
1353                 err_str = 'size of API enum %s is wrong' % o.name
1354                 write('STATIC_ASSERT(%s == %s, "%s");\n'
1355                       % (size1, size2, err_str))
1356         else:
1357             if tname == 'Union':
1358                 write("typedef union __attribute__ ((packed)) _vl_api_%s {\n"
1359                       % o.name)
1360             else:
1361                 write(("typedef struct __attribute__ ((packed)) _vl_api_%s {\n")
1362                       % o.name)
1363             for b in o.block:
1364                 if b.type == 'Option':
1365                     continue
1366                 if b.type == 'Field':
1367                     write("    %s %s;\n" % (api2c(b.fieldtype),
1368                                             b.fieldname))
1369                 elif b.type == 'Array':
1370                     if b.lengthfield:
1371                         write("    %s %s[0];\n" % (api2c(b.fieldtype),
1372                                                    b.fieldname))
1373                     else:
1374                         # Fixed length strings decay to nul terminated u8
1375                         if b.fieldtype == 'string':
1376                             if b.modern_vla:
1377                                 write('    {} {};\n'
1378                                       .format(api2c(b.fieldtype),
1379                                               b.fieldname))
1380                             else:
1381                                 write('    u8 {}[{}];\n'
1382                                       .format(b.fieldname, b.length))
1383                         else:
1384                             write("    %s %s[%s];\n" %
1385                                   (api2c(b.fieldtype), b.fieldname,
1386                                    b.length))
1387                 else:
1388                     raise ValueError("Error in processing type {} for {}"
1389                                      .format(b, o.name))
1390
1391             write('} vl_api_%s_t;\n' % o.name)
1392             write(f'#define VL_API_{o.name.upper()}_IS_CONSTANT_SIZE ({0 if o.vla else 1})\n\n')
1393
1394     for t in s['Define']:
1395         write('#define VL_API_{ID}_CRC "{n}_{crc:08x}"\n'
1396               .format(n=t.name, ID=t.name.upper(), crc=t.crc))
1397
1398     write("\n#endif\n")
1399
1400
1401 def generate_c_boilerplate(services, defines, counters, file_crc,
1402                            module, stream):
1403     '''VPP side plugin.'''
1404     write = stream.write
1405     define_hash = {d.name: d for d in defines}
1406
1407     hdr = '''\
1408 #define vl_endianfun            /* define message structures */
1409 #include "{module}.api.h"
1410 #undef vl_endianfun
1411
1412 #define vl_calcsizefun
1413 #include "{module}.api.h"
1414 #undef vl_calsizefun
1415
1416 /* instantiate all the print functions we know about */
1417 #define vl_print(handle, ...) vlib_cli_output (handle, __VA_ARGS__)
1418 #define vl_printfun
1419 #include "{module}.api.h"
1420 #undef vl_printfun
1421
1422 '''
1423
1424     write(hdr.format(module=module))
1425     write('static u16\n')
1426     write('setup_message_id_table (void) {\n')
1427     write('   api_main_t *am = my_api_main;\n')
1428     write('   vl_msg_api_msg_config_t c;\n')
1429     write('   u16 msg_id_base = vl_msg_api_get_msg_ids ("{}_{crc:08x}", '
1430           'VL_MSG_{m}_LAST);\n'
1431           .format(module, crc=file_crc, m=module.upper()))
1432
1433     for d in defines:
1434         write('   vl_msg_api_add_msg_name_crc (am, "{n}_{crc:08x}",\n'
1435               '                                VL_API_{ID} + msg_id_base);\n'
1436               .format(n=d.name, ID=d.name.upper(), crc=d.crc))
1437     for s in services:
1438         d = define_hash[s.caller]
1439         write('   c = (vl_msg_api_msg_config_t) '
1440               ' {{.id = VL_API_{ID} + msg_id_base,\n'
1441               '   .name = "{n}",\n'
1442               '   .handler = vl_api_{n}_t_handler,\n'
1443               '   .cleanup = vl_noop_handler,\n'
1444               '   .endian = vl_api_{n}_t_endian,\n'
1445               '   .print = vl_api_{n}_t_print,\n'
1446               '   .traced = 1,\n'
1447               '   .replay = 1,\n'
1448               '   .print_json = vl_api_{n}_t_print_json,\n'
1449               '   .tojson = vl_api_{n}_t_tojson,\n'
1450               '   .fromjson = vl_api_{n}_t_fromjson,\n'
1451               '   .calc_size = vl_api_{n}_t_calc_size,\n'
1452               '   .is_autoendian = {auto}}};\n'
1453               .format(n=s.caller, ID=s.caller.upper(),
1454                       auto=d.autoendian))
1455         write('   vl_msg_api_config (&c);\n')
1456         try:
1457             d = define_hash[s.reply]
1458             write('   c = (vl_msg_api_msg_config_t) '
1459                   '{{.id = VL_API_{ID} + msg_id_base,\n'
1460                   '  .name = "{n}",\n'
1461                   '  .handler = 0,\n'
1462                   '  .cleanup = vl_noop_handler,\n'
1463                   '  .endian = vl_api_{n}_t_endian,\n'
1464                   '  .print = vl_api_{n}_t_print,\n'
1465                   '  .traced = 1,\n'
1466                   '  .replay = 1,\n'
1467                   '  .print_json = vl_api_{n}_t_print_json,\n'
1468                   '  .tojson = vl_api_{n}_t_tojson,\n'
1469                   '  .fromjson = vl_api_{n}_t_fromjson,\n'
1470                   '  .calc_size = vl_api_{n}_t_calc_size,\n'
1471                   '  .is_autoendian = {auto}}};\n'
1472                   .format(n=s.reply, ID=s.reply.upper(),
1473                           auto=d.autoendian))
1474             write('   vl_msg_api_config (&c);\n')
1475         except KeyError:
1476             pass
1477
1478     write('   return msg_id_base;\n')
1479     write('}\n')
1480
1481     severity = {'error': 'VL_COUNTER_SEVERITY_ERROR',
1482                 'info': 'VL_COUNTER_SEVERITY_INFO',
1483                 'warn': 'VL_COUNTER_SEVERITY_WARN'}
1484
1485     for cnt in counters:
1486         csetname = cnt.name
1487         write('vlib_error_desc_t {}_error_counters[] = {{\n'.format(csetname))
1488         for c in cnt.block:
1489             write('  {\n')
1490             write('   .name = "{}",\n'.format(c['name']))
1491             write('   .desc = "{}",\n'.format(c['description']))
1492             write('   .severity = {},\n'.format(severity[c['severity']]))
1493             write('  },\n')
1494         write('};\n')
1495
1496
1497 def generate_c_test_boilerplate(services, defines, file_crc, module, plugin,
1498                                 stream):
1499     '''Generate code for legacy style VAT. To be deleted.'''
1500     write = stream.write
1501
1502     define_hash = {d.name: d for d in defines}
1503
1504     hdr = '''\
1505 #define vl_endianfun            /* define message structures */
1506 #include "{module}.api.h"
1507 #undef vl_endianfun
1508
1509 #define vl_calcsizefun
1510 #include "{module}.api.h"
1511 #undef vl_calsizefun
1512
1513 /* instantiate all the print functions we know about */
1514 #define vl_print(handle, ...) vlib_cli_output (handle, __VA_ARGS__)
1515 #define vl_printfun
1516 #include "{module}.api.h"
1517 #undef vl_printfun
1518
1519 '''
1520
1521     write(hdr.format(module=module))
1522     for s in services:
1523         try:
1524             d = define_hash[s.reply]
1525         except KeyError:
1526             continue
1527         if d.manual_print:
1528             write('/*\n'
1529                   ' * Manual definition requested for: \n'
1530                   ' * vl_api_{n}_t_handler()\n'
1531                   ' */\n'
1532                   .format(n=s.reply))
1533             continue
1534         if not define_hash[s.caller].autoreply:
1535             write('/* Generation not supported (vl_api_{n}_t_handler()) */\n'
1536                   .format(n=s.reply))
1537             continue
1538         write('#ifndef VL_API_{n}_T_HANDLER\n'.format(n=s.reply.upper()))
1539         write('static void\n')
1540         write('vl_api_{n}_t_handler (vl_api_{n}_t * mp) {{\n'
1541               .format(n=s.reply))
1542         write('   vat_main_t * vam = {}_test_main.vat_main;\n'.format(module))
1543         write('   i32 retval = ntohl(mp->retval);\n')
1544         write('   if (vam->async_mode) {\n')
1545         write('      vam->async_errors += (retval < 0);\n')
1546         write('   } else {\n')
1547         write('      vam->retval = retval;\n')
1548         write('      vam->result_ready = 1;\n')
1549         write('   }\n')
1550         write('}\n')
1551         write('#endif\n')
1552
1553         for e in s.events:
1554             if define_hash[e].manual_print:
1555                 continue
1556             write('static void\n')
1557             write('vl_api_{n}_t_handler (vl_api_{n}_t * mp) {{\n'.format(n=e))
1558             write('    vl_print(0, "{n} event called:");\n'.format(n=e))
1559             write('    vl_api_{n}_t_print(mp, 0);\n'.format(n=e))
1560             write('}\n')
1561
1562     write('static void\n')
1563     write('setup_message_id_table (vat_main_t * vam, u16 msg_id_base) {\n')
1564     for s in services:
1565         write('   vl_msg_api_set_handlers(VL_API_{ID} + msg_id_base, '
1566               '                           "{n}",\n'
1567               '                           vl_api_{n}_t_handler, '
1568               '                           vl_noop_handler,\n'
1569               '                           vl_api_{n}_t_endian, '
1570               '                           vl_api_{n}_t_print,\n'
1571               '                           sizeof(vl_api_{n}_t), 1,\n'
1572               '                           vl_api_{n}_t_print_json,\n'
1573               '                           vl_api_{n}_t_tojson,\n'
1574               '                           vl_api_{n}_t_fromjson,\n'
1575               '                           vl_api_{n}_t_calc_size);\n'
1576               .format(n=s.reply, ID=s.reply.upper()))
1577         write('   hash_set_mem (vam->function_by_name, "{n}", api_{n});\n'
1578               .format(n=s.caller))
1579         try:
1580             write('   hash_set_mem (vam->help_by_name, "{n}", "{help}");\n'
1581                   .format(n=s.caller,
1582                           help=define_hash[s.caller].options['vat_help']))
1583         except KeyError:
1584             pass
1585
1586         # Events
1587         for e in s.events:
1588             write('   vl_msg_api_set_handlers(VL_API_{ID} + msg_id_base, '
1589                   '                          "{n}",\n'
1590                   '                           vl_api_{n}_t_handler, '
1591                   '                           vl_noop_handler,\n'
1592                   '                           vl_api_{n}_t_endian, '
1593                   '                           vl_api_{n}_t_print,\n'
1594                   '                           sizeof(vl_api_{n}_t), 1,\n'
1595                   '                           vl_api_{n}_t_print_json,\n'
1596                   '                           vl_api_{n}_t_tojson,\n'
1597                   '                           vl_api_{n}_t_fromjson,\n'
1598                   '                           vl_api_{n}_t_calc_size);\n'
1599                   .format(n=e, ID=e.upper()))
1600
1601     write('}\n')
1602     write('clib_error_t * vat_plugin_register (vat_main_t *vam)\n')
1603     write('{\n')
1604     write('   {n}_test_main_t * mainp = &{n}_test_main;\n'.format(n=module))
1605     write('   mainp->vat_main = vam;\n')
1606     write('   mainp->msg_id_base = vl_client_get_first_plugin_msg_id '
1607           '                       ("{n}_{crc:08x}");\n'
1608           .format(n=module, crc=file_crc))
1609     write('   if (mainp->msg_id_base == (u16) ~0)\n')
1610     write('      return clib_error_return (0, "{} plugin not loaded...");\n'
1611           .format(module))
1612     write('   setup_message_id_table (vam, mainp->msg_id_base);\n')
1613     write('#ifdef VL_API_LOCAL_SETUP_MESSAGE_ID_TABLE\n')
1614     write('    VL_API_LOCAL_SETUP_MESSAGE_ID_TABLE(vam);\n')
1615     write('#endif\n')
1616     write('   return 0;\n')
1617     write('}\n')
1618
1619
1620 def apifunc(func):
1621     '''Check if a method is generated already.'''
1622     def _f(module, d, processed, *args):
1623         if d.name in processed:
1624             return None
1625         processed[d.name] = True
1626         return func(module, d, *args)
1627     return _f
1628
1629
1630 def c_test_api_service(s, dump, stream):
1631     '''Generate JSON code for a service.'''
1632     write = stream.write
1633
1634     req_reply_template = '''\
1635 static cJSON *
1636 api_{n} (cJSON *o)
1637 {{
1638   vl_api_{n}_t *mp;
1639   int len;
1640   if (!o) return 0;
1641   mp = vl_api_{n}_t_fromjson(o, &len);
1642   if (!mp) {{
1643     fprintf(stderr, "Failed converting JSON to API\\n");
1644     return 0;
1645   }}
1646
1647   mp->_vl_msg_id = vac_get_msg_index(VL_API_{N}_CRC);
1648   vl_api_{n}_t_endian(mp);
1649   vac_write((char *)mp, len);
1650   cJSON_free(mp);
1651
1652   /* Read reply */
1653   char *p;
1654   int l;
1655   vac_read(&p, &l, 5); // XXX: Fix timeout
1656   if (p == 0 || l == 0) return 0;
1657     // XXX Will fail in case of event received. Do loop
1658   if (ntohs(*((u16 *)p)) != vac_get_msg_index(VL_API_{R}_CRC)) {{
1659     fprintf(stderr, "Mismatched reply\\n");
1660     return 0;
1661   }}
1662   vl_api_{r}_t *rmp = (vl_api_{r}_t *)p;
1663   vl_api_{r}_t_endian(rmp);
1664   return vl_api_{r}_t_tojson(rmp);
1665 }}
1666
1667 '''
1668     dump_details_template = '''\
1669 static cJSON *
1670 api_{n} (cJSON *o)
1671 {{
1672   u16 msg_id = vac_get_msg_index(VL_API_{N}_CRC);
1673   int len;
1674   if (!o) return 0;
1675   vl_api_{n}_t *mp = vl_api_{n}_t_fromjson(o, &len);
1676   if (!mp) {{
1677       fprintf(stderr, "Failed converting JSON to API\\n");
1678       return 0;
1679   }}
1680   mp->_vl_msg_id = msg_id;
1681   vl_api_{n}_t_endian(mp);
1682   vac_write((char *)mp, len);
1683   cJSON_free(mp);
1684
1685   vat2_control_ping(123); // FIX CONTEXT
1686   cJSON *reply = cJSON_CreateArray();
1687
1688   u16 ping_reply_msg_id = vac_get_msg_index(VL_API_CONTROL_PING_REPLY_CRC);
1689   u16 details_msg_id = vac_get_msg_index(VL_API_{R}_CRC);
1690
1691   while (1) {{
1692     /* Read reply */
1693     char *p;
1694     int l;
1695     vac_read(&p, &l, 5); // XXX: Fix timeout
1696     if (p == 0 || l == 0) {{
1697       cJSON_free(reply);
1698       return 0;
1699     }}
1700
1701     /* Message can be one of [_details, control_ping_reply
1702      * or unrelated event]
1703      */
1704     u16 reply_msg_id = ntohs(*((u16 *)p));
1705     if (reply_msg_id == ping_reply_msg_id) {{
1706         break;
1707     }}
1708
1709     if (reply_msg_id == details_msg_id) {{
1710         if (l < sizeof(vl_api_{r}_t)) {{
1711             cJSON_free(reply);
1712             return 0;
1713         }}
1714         vl_api_{r}_t *rmp = (vl_api_{r}_t *)p;
1715         vl_api_{r}_t_endian(rmp);
1716         cJSON_AddItemToArray(reply, vl_api_{r}_t_tojson(rmp));
1717     }}
1718   }}
1719   return reply;
1720 }}
1721
1722 '''
1723     gets_details_reply_template = '''\
1724 static cJSON *
1725 api_{n} (cJSON *o)
1726 {{
1727     u16 msg_id = vac_get_msg_index(VL_API_{N}_CRC);
1728   int len = 0;
1729   if (!o) return 0;
1730   vl_api_{n}_t *mp = vl_api_{n}_t_fromjson(o, &len);
1731   if (!mp) {{
1732     fprintf(stderr, "Failed converting JSON to API\\n");
1733     return 0;
1734   }}
1735   mp->_vl_msg_id = msg_id;
1736
1737   vl_api_{n}_t_endian(mp);
1738   vac_write((char *)mp, len);
1739   cJSON_free(mp);
1740
1741   cJSON *reply = cJSON_CreateArray();
1742
1743   u16 reply_msg_id = vac_get_msg_index(VL_API_{R}_CRC);
1744   u16 details_msg_id = vac_get_msg_index(VL_API_{D}_CRC);
1745
1746   while (1) {{
1747     /* Read reply */
1748     char *p;
1749     int l;
1750     vac_read(&p, &l, 5); // XXX: Fix timeout
1751
1752     /* Message can be one of [_details, control_ping_reply
1753      * or unrelated event]
1754      */
1755     u16 msg_id = ntohs(*((u16 *)p));
1756     if (msg_id == reply_msg_id) {{
1757         vl_api_{r}_t *rmp = (vl_api_{r}_t *)p;
1758         vl_api_{r}_t_endian(rmp);
1759         cJSON_AddItemToArray(reply, vl_api_{r}_t_tojson(rmp));
1760         break;
1761     }}
1762
1763     if (msg_id == details_msg_id) {{
1764         vl_api_{d}_t *rmp = (vl_api_{d}_t *)p;
1765         vl_api_{d}_t_endian(rmp);
1766         cJSON_AddItemToArray(reply, vl_api_{d}_t_tojson(rmp));
1767     }}
1768   }}
1769   return reply;
1770 }}
1771
1772 '''
1773
1774     if dump:
1775         if s.stream_message:
1776             write(gets_details_reply_template
1777                   .format(n=s.caller, r=s.reply, N=s.caller.upper(),
1778                           R=s.reply.upper(), d=s.stream_message,
1779                           D=s.stream_message.upper()))
1780         else:
1781             write(dump_details_template.format(n=s.caller, r=s.reply,
1782                                                N=s.caller.upper(),
1783                                                R=s.reply.upper()))
1784     else:
1785         write(req_reply_template.format(n=s.caller, r=s.reply,
1786                                         N=s.caller.upper(),
1787                                         R=s.reply.upper()))
1788
1789
1790 def generate_c_test2_boilerplate(services, defines, module, stream):
1791     '''Generate code for VAT2 plugin.'''
1792     write = stream.write
1793
1794     define_hash = {d.name: d for d in defines}
1795     # replies = {}
1796
1797     hdr = '''\
1798 #include <vlibapi/api.h>
1799 #include <vlibmemory/api.h>
1800 #include <vppinfra/error.h>
1801 #include <vnet/ip/ip_format_fns.h>
1802 #include <vnet/ethernet/ethernet_format_fns.h>
1803
1804 #define vl_typedefs             /* define message structures */
1805 #include <vlibmemory/vl_memory_api_h.h>
1806 #include <vlibmemory/vlib.api_types.h>
1807 #include <vlibmemory/vlib.api.h>
1808 #undef vl_typedefs
1809
1810 #include "{module}.api_enum.h"
1811 #include "{module}.api_types.h"
1812
1813 #define vl_endianfun            /* define message structures */
1814 #include "{module}.api.h"
1815 #undef vl_endianfun
1816
1817 #define vl_calcsizefun
1818 #include "{module}.api.h"
1819 #undef vl_calsizefun
1820
1821 #define vl_print(handle, ...) vlib_cli_output (handle, __VA_ARGS__)
1822 #define vl_printfun
1823 #include "{module}.api.h"
1824 #undef vl_printfun
1825
1826 #include "{module}.api_tojson.h"
1827 #include "{module}.api_fromjson.h"
1828 #include <vpp-api/client/vppapiclient.h>
1829
1830 #include <vat2/vat2_helpers.h>
1831
1832 '''
1833
1834     write(hdr.format(module=module))
1835
1836     for s in services:
1837         if s.reply not in define_hash:
1838             continue
1839         c_test_api_service(s, s.stream, stream)
1840
1841     write('void vat2_register_function(char *, cJSON * (*)(cJSON *), cJSON * (*)(void *), u32);\n')
1842     # write('__attribute__((constructor))')
1843     write('clib_error_t *\n')
1844     write('vat2_register_plugin (void) {\n')
1845     for s in services:
1846         if s.reply not in define_hash:
1847             continue
1848         crc = define_hash[s.caller].crc
1849         write('   vat2_register_function("{n}", api_{n}, (cJSON * (*)(void *))vl_api_{n}_t_tojson, 0x{crc:08x});\n'
1850               .format(n=s.caller, crc=crc))
1851     write('   return 0;\n')
1852     write('}\n')
1853
1854
1855 #
1856 # Plugin entry point
1857 #
1858 def run(args, apifilename, s):
1859     '''Main plugin entry point.'''
1860     stream = StringIO()
1861
1862     if not args.outputdir:
1863         sys.stderr.write('Missing --outputdir argument')
1864         return None
1865
1866     basename = os.path.basename(apifilename)
1867     filename, _ = os.path.splitext(basename)
1868     modulename = filename.replace('.', '_')
1869     filename_enum = os.path.join(args.outputdir + '/' + basename + '_enum.h')
1870     filename_types = os.path.join(args.outputdir + '/' + basename + '_types.h')
1871     filename_c = os.path.join(args.outputdir + '/' + basename + '.c')
1872     filename_c_test = os.path.join(args.outputdir + '/' + basename + '_test.c')
1873     filename_c_test2 = (os.path.join(args.outputdir + '/' + basename +
1874                                      '_test2.c'))
1875     filename_c_tojson = (os.path.join(args.outputdir +
1876                                       '/' + basename + '_tojson.h'))
1877     filename_c_fromjson = (os.path.join(args.outputdir + '/' +
1878                                         basename + '_fromjson.h'))
1879
1880     # Generate separate types file
1881     st = StringIO()
1882     generate_include_types(s, modulename, st)
1883     with open(filename_types, 'w') as fd:
1884         st.seek(0)
1885         shutil.copyfileobj(st, fd)
1886     st.close()
1887
1888     # Generate separate enum file
1889     st = StringIO()
1890     st.write('#ifndef included_{}_api_enum_h\n'.format(modulename))
1891     st.write('#define included_{}_api_enum_h\n'.format(modulename))
1892     generate_include_enum(s, modulename, st)
1893     generate_include_counters(s['Counters'], st)
1894     st.write('#endif\n')
1895     with open(filename_enum, 'w') as fd:
1896         st.seek(0)
1897         shutil.copyfileobj(st, fd)
1898     st.close()
1899
1900     # Generate separate C file
1901     st = StringIO()
1902     generate_c_boilerplate(s['Service'], s['Define'], s['Counters'],
1903                            s['file_crc'], modulename, st)
1904     with open(filename_c, 'w') as fd:
1905         st.seek(0)
1906         shutil.copyfileobj(st, fd)
1907     st.close()
1908
1909     # Generate separate C test file
1910     st = StringIO()
1911     plugin = bool('plugin' in apifilename)
1912     generate_c_test_boilerplate(s['Service'], s['Define'],
1913                                 s['file_crc'],
1914                                 modulename, plugin, st)
1915     with open(filename_c_test, 'w') as fd:
1916         st.seek(0)
1917         shutil.copyfileobj(st, fd)
1918     st.close()
1919
1920     # Fully autogenerated VATv2 C test file
1921     st = StringIO()
1922     generate_c_test2_boilerplate(s['Service'], s['Define'],
1923                                  modulename, st)
1924     with open(filename_c_test2, 'w') as fd:
1925         st.seek(0)
1926         shutil.copyfileobj(st, fd)
1927     st.close()                  #
1928
1929     # Generate separate JSON file
1930     st = StringIO()
1931     generate_tojson(s, modulename, st)
1932     with open(filename_c_tojson, 'w') as fd:
1933         st.seek(0)
1934         shutil.copyfileobj(st, fd)
1935     st.close()
1936     st = StringIO()
1937     generate_fromjson(s, modulename, st)
1938     with open(filename_c_fromjson, 'w') as fd:
1939         st.seek(0)
1940         shutil.copyfileobj(st, fd)
1941     st.close()
1942
1943     output = TOP_BOILERPLATE.format(datestring=DATESTRING,
1944                                     input_filename=basename)
1945     output += generate_imports(s['Import'])
1946     output += msg_ids(s)
1947     output += msg_names(s)
1948     output += msg_name_crc_list(s, filename)
1949     output += typedefs(modulename)
1950     printfun_types(s['types'], stream, modulename)
1951     printfun(s['Define'], stream, modulename)
1952     output += stream.getvalue()
1953     stream.close()
1954     output += endianfun(s['types'] + s['Define'], modulename)
1955     output += calc_size_fun(s['types'] + s['Define'], modulename)
1956     output += version_tuple(s, basename)
1957     output += BOTTOM_BOILERPLATE.format(input_filename=basename,
1958                                         file_crc=s['file_crc'])
1959
1960     return output