api: verify message size on receipt
[vpp.git] / src / tools / vppapigen / vppapigen_c.py
index 66e0c2f..f93e898 100644 (file)
@@ -24,6 +24,7 @@ VAT2 tests.
 '''
 
 import datetime
+import itertools
 import os
 import time
 import sys
@@ -66,16 +67,17 @@ class ToJSON():
         write('#ifndef included_{}_api_tojson_h\n'.format(self.module))
         write('#define included_{}_api_tojson_h\n'.format(self.module))
         write('#include <vppinfra/cJSON.h>\n\n')
-        write('#include <vat2/jsonconvert.h>\n\n')
+        write('#include <vppinfra/jsonformat.h>\n\n')
+        if self.module == 'interface_types':
+            write('#define vl_printfun\n')
+            write('#include <vnet/interface_types.api.h>\n\n')
 
     def footer(self):
         '''Output the bottom boilerplate.'''
         write = self.stream.write
         write('#endif\n')
 
-    def get_json_func(self, t):
-        '''Given the type, returns the function to use to create a
-        cJSON object'''
+    def get_base_type(self, t):
         vt_type = None
         try:
             vt = self.types_hash[t]
@@ -83,6 +85,12 @@ class ToJSON():
                 vt_type = vt.alias['type']
         except KeyError:
             vt = t
+        return vt, vt_type
+
+    def get_json_func(self, t):
+        '''Given the type, returns the function to use to create a
+        cJSON object'''
+        vt, vt_type = self.get_base_type(t)
 
         if t in self.is_number or vt_type in self.is_number:
             return 'cJSON_AddNumberToObject', '', False
@@ -101,6 +109,9 @@ class ToJSON():
             return 'cJSON_CreateNumber', ''
         if t == 'bool':
             return 'cJSON_CreateBool', ''
+        vt, vt_type = self.get_base_type(t)
+        if vt.type == 'Enum' or vt.type == 'EnumFlag':
+            return '{t}_tojson'.format(t=t), ''
         return '{t}_tojson'.format(t=t), '&'
 
     def print_string(self, o):
@@ -196,8 +207,11 @@ class ToJSON():
         write('    cJSON *array = cJSON_CreateArray();\n')
 
         for b in o.block:
+            if b[1] == 0:
+                    continue
             write('    if (a & {})\n'.format(b[0]))
-            write('       cJSON_AddItemToArray(array, cJSON_CreateString("{}"));\n'.format(b[0]))
+            write(
+                '       cJSON_AddItemToArray(array, cJSON_CreateString("{}"));\n'.format(b[0]))
         write('    return array;\n')
         write('}\n')
 
@@ -224,6 +238,8 @@ class ToJSON():
         write('    cJSON *o = cJSON_CreateObject();\n')
         write('    cJSON_AddStringToObject(o, "_msgname", "{}");\n'
               .format(o.name))
+        write('    cJSON_AddStringToObject(o, "_crc", "{crc:08x}");\n'
+              .format(crc=o.crc))
 
         for t in o.block:
             self._dispatch[t.type](self, t)
@@ -305,7 +321,8 @@ class FromJSON():
         write('#ifndef included_{}_api_fromjson_h\n'.format(self.module))
         write('#define included_{}_api_fromjson_h\n'.format(self.module))
         write('#include <vppinfra/cJSON.h>\n\n')
-        write('#include <vat2/jsonconvert.h>\n\n')
+        write('#include <vppinfra/jsonformat.h>\n\n')
+        write('#pragma GCC diagnostic ignored "-Wunused-label"\n')
 
     def is_base_type(self, t):
         '''Check if a type is one of the VPP API base types'''
@@ -324,14 +341,15 @@ class FromJSON():
         '''Convert JSON string to vl_api_string_t'''
         write = self.stream.write
 
-        msgvar = "a" if toplevel else "mp"
+        msgvar = "a" if toplevel else "*mp"
         msgsize = "l" if toplevel else "*len"
 
         if o.modern_vla:
             write('    char *p = cJSON_GetStringValue(item);\n')
             write('    size_t plen = strlen(p);\n')
-            write('    {msgvar} = realloc({msgvar}, {msgsize} + plen);\n'
+            write('    {msgvar} = cJSON_realloc({msgvar}, {msgsize} + plen, {msgsize});\n'
                   .format(msgvar=msgvar, msgsize=msgsize))
+            write('    if ({msgvar} == 0) goto error;\n'.format(msgvar=msgvar))
             write('    vl_api_c_string_to_api_string(p, (void *){msgvar} + '
                   '{msgsize} - sizeof(vl_api_string_t));\n'
                   .format(msgvar=msgvar, msgsize=msgsize))
@@ -344,25 +362,21 @@ class FromJSON():
     def print_field(self, o, toplevel=False):
         '''Called for every field in a typedef or define.'''
         write = self.stream.write
-        write('    // start field {}\n'.format(o.fieldname))
         if o.fieldname in self.noprint_fields:
             return
         is_bt = self.is_base_type(o.fieldtype)
         t = 'vl_api_{}'.format(o.fieldtype) if is_bt else o.fieldtype
 
-        msgvar = "a" if toplevel else "mp"
+        msgvar = "(void **)&a" if toplevel else "mp"
         msgsize = "&l" if toplevel else "len"
 
         if is_bt:
             write('    vl_api_{t}_fromjson(item, &a->{n});\n'
                   .format(t=o.fieldtype, n=o.fieldname))
         else:
-            write('    {msgvar} = {t}_fromjson({msgvar}, '
-                  '{msgsize}, item, &a->{n});\n'
+            write('    if ({t}_fromjson({msgvar}, '
+                  '{msgsize}, item, &a->{n}) < 0) goto error;\n'
                   .format(t=t, n=o.fieldname, msgvar=msgvar, msgsize=msgsize))
-            write('    if (!{msgvar}) return 0;\n'.format(msgvar=msgvar))
-
-        write('    // end field {}\n'.format(o.fieldname))
 
     _dispatch['Field'] = print_field
 
@@ -375,7 +389,7 @@ class FromJSON():
         int i;
         cJSON *array = cJSON_GetObjectItem(o, "{n}");
         int size = cJSON_GetArraySize(array);
-        if (size != {lfield}) return 0;
+        if (size != {lfield}) goto error;
         for (i = 0; i < size; i++) {{
             cJSON *e = cJSON_GetArrayItem(array, i);
             {call}
@@ -388,8 +402,8 @@ class FromJSON():
         cJSON *array = cJSON_GetObjectItem(o, "{n}");
         int size = cJSON_GetArraySize(array);
         {lfield} = size;
-        {msgvar} = realloc({msgvar}, {msgsize} + sizeof({t}) * size);
-        {t} *d = (void *){msgvar} + {msgsize};
+        {realloc} = cJSON_realloc({realloc}, {msgsize} + sizeof({t}) * size, {msgsize});
+        {t} *d = (void *){realloc} + {msgsize};
         {msgsize} += sizeof({t}) * size;
         for (i = 0; i < size; i++) {{
             cJSON *e = cJSON_GetArrayItem(array, i);
@@ -403,25 +417,26 @@ class FromJSON():
             return
 
         lfield = 'a->' + o.lengthfield if o.lengthfield else o.length
-        msgvar = "a" if toplevel else "mp"
+        msgvar = "(void **)&a" if toplevel else "mp"
+        realloc = "a" if toplevel else "*mp"
         msgsize = "l" if toplevel else "*len"
 
         if o.fieldtype == 'u8':
             if o.lengthfield:
                 write('    s = u8string_fromjson(o, "{}");\n'
                       .format(o.fieldname))
-                write('    if (!s) return 0;\n')
+                write('    if (!s) goto error;\n')
                 write('    {} = vec_len(s);\n'.format(lfield))
 
-                write('    {msgvar} = realloc({msgvar}, {msgsize} + '
-                      'vec_len(s));\n'.format(msgvar=msgvar, msgsize=msgsize))
-                write('    memcpy((void *){msgvar} + {msgsize}, s, '
-                      'vec_len(s));\n'.format(msgvar=msgvar, msgsize=msgsize))
+                write('    {realloc} = cJSON_realloc({realloc}, {msgsize} + '
+                      'vec_len(s), {msgsize});\n'.format(msgvar=msgvar, msgsize=msgsize, realloc=realloc))
+                write('    memcpy((void *){realloc} + {msgsize}, s, '
+                      'vec_len(s));\n'.format(realloc=realloc, msgsize=msgsize))
                 write('    {msgsize} += vec_len(s);\n'.format(msgsize=msgsize))
 
                 write('    vec_free(s);\n')
             else:
-                write('    u8string_fromjson2(o, "{n}", a->{n});\n'
+                write('    if (u8string_fromjson2(o, "{n}", a->{n}) < 0) goto error;\n'
                       .format(n=o.fieldname))
             return
 
@@ -432,26 +447,27 @@ class FromJSON():
                 call = ('vl_api_{t}_fromjson(e, &d[i]);'
                         .format(t=o.fieldtype))
             else:
-                call = ('{t}_fromjson({msgvar}, len, e, &d[i]); '
+                call = ('if ({t}_fromjson({msgvar}, len, e, &d[i]) < 0) goto error; '
                         .format(t=o.fieldtype, msgvar=msgvar))
             write(forloop_vla.format(lfield=lfield,
                                      t=o.fieldtype,
                                      n=o.fieldname,
                                      call=call,
-                                     msgvar=msgvar,
+                                     realloc=realloc,
                                      msgsize=msgsize))
         else:
             if is_bt:
                 call = ('vl_api_{t}_fromjson(e, &a->{n}[i]);'
                         .format(t=t, n=o.fieldname))
             else:
-                call = ('a = {}_fromjson({}, len, e, &a->{}[i]);'
+                call = ('if ({}_fromjson({}, len, e, &a->{}[i]) < 0) goto error;'
                         .format(t, msgvar, o.fieldname))
             write(forloop.format(lfield=lfield,
                                  t=t,
                                  n=o.fieldname,
                                  call=call,
                                  msgvar=msgvar,
+                                 realloc=realloc,
                                  msgsize=msgsize))
 
     _dispatch['Array'] = print_array
@@ -459,14 +475,15 @@ class FromJSON():
     def print_enum(self, o):
         '''Convert to JSON enum(string) to VPP API enum (int)'''
         write = self.stream.write
-        write('static inline void *vl_api_{n}_t_fromjson '
-              '(void *mp, int *len, cJSON *o, vl_api_{n}_t *a) {{\n'
+        write('static inline int vl_api_{n}_t_fromjson'
+              '(void **mp, int *len, cJSON *o, vl_api_{n}_t *a) {{\n'
               .format(n=o.name))
         write('    char *p = cJSON_GetStringValue(o);\n')
         for b in o.block:
-            write('    if (strcmp(p, "{}") == 0) {{*a = {}; return mp;}}\n'
+            write('    if (strcmp(p, "{}") == 0) {{*a = {}; return 0;}}\n'
                   .format(b[0], b[1]))
-        write('   return 0;\n')
+        write('    *a = 0;\n')
+        write('    return -1;\n')
         write('}\n')
 
     _dispatch['Enum'] = print_enum
@@ -474,20 +491,20 @@ class FromJSON():
     def print_enum_flag(self, o):
         '''Convert to JSON enum(string) to VPP API enum (int)'''
         write = self.stream.write
-        write('static inline void *vl_api_{n}_t_fromjson '
-              '(void *mp, int *len, cJSON *o, vl_api_{n}_t *a) {{\n'
+        write('static inline int vl_api_{n}_t_fromjson '
+              '(void **mp, int *len, cJSON *o, vl_api_{n}_t *a) {{\n'
               .format(n=o.name))
         write('   int i;\n')
         write('   *a = 0;\n')
         write('   for (i = 0; i < cJSON_GetArraySize(o); i++) {\n')
         write('       cJSON *e = cJSON_GetArrayItem(o, i);\n')
         write('       char *p = cJSON_GetStringValue(e);\n')
-        write('       if (!p) return 0;\n')
+        write('       if (!p) return -1;\n')
         for b in o.block:
             write('       if (strcmp(p, "{}") == 0) *a |= {};\n'
                   .format(b[0], b[1]))
         write('    }\n')
-        write('   return mp;\n')
+        write('   return 0;\n')
         write('}\n')
 
     _dispatch['EnumFlag'] = print_enum_flag
@@ -496,7 +513,7 @@ class FromJSON():
         '''Convert from JSON object to VPP API binary representation'''
         write = self.stream.write
 
-        write('static inline void *vl_api_{name}_t_fromjson (void *mp, '
+        write('static inline int vl_api_{name}_t_fromjson (void **mp, '
               'int *len, cJSON *o, vl_api_{name}_t *a) {{\n'
               .format(name=o.name))
         write('    cJSON *item __attribute__ ((unused));\n')
@@ -504,20 +521,21 @@ class FromJSON():
         for t in o.block:
             if t.type == 'Field' and t.is_lengthfield:
                 continue
-            write('    item = cJSON_GetObjectItem(o, "{}");\n'
+            write('\n    item = cJSON_GetObjectItem(o, "{}");\n'
                   .format(t.fieldname))
-            write('    if (!item) return 0;\n')
-
+            write('    if (!item) goto error;\n')
             self._dispatch[t.type](self, t)
 
-        write('    return mp;\n')
+        write('\n    return 0;\n')
+        write('\n  error:\n')
+        write('    return -1;\n')
         write('}\n')
 
     def print_union(self, o):
         '''Convert JSON object to VPP API binary union'''
         write = self.stream.write
 
-        write('static inline void *vl_api_{name}_t_fromjson (void *mp, '
+        write('static inline int vl_api_{name}_t_fromjson (void **mp, '
               'int *len, cJSON *o, vl_api_{name}_t *a) {{\n'
               .format(name=o.name))
         write('    cJSON *item __attribute__ ((unused));\n')
@@ -530,36 +548,42 @@ class FromJSON():
             write('    if (item) {\n')
             self._dispatch[t.type](self, t)
             write('    };\n')
-        write('    return mp;\n')
+        write('\n    return 0;\n')
+        write('\n  error:\n')
+        write('    return -1;\n')
         write('}\n')
 
     def print_define(self, o):
         '''Convert JSON object to VPP API message'''
         write = self.stream.write
+        error = 0
         write('static inline vl_api_{name}_t *vl_api_{name}_t_fromjson '
               '(cJSON *o, int *len) {{\n'.format(name=o.name))
         write('    cJSON *item __attribute__ ((unused));\n')
         write('    u8 *s __attribute__ ((unused));\n')
         write('    int l = sizeof(vl_api_{}_t);\n'.format(o.name))
-        write('    vl_api_{}_t *a = malloc(l);\n'.format(o.name))
+        write('    vl_api_{}_t *a = cJSON_malloc(l);\n'.format(o.name))
+        write('\n')
 
         for t in o.block:
             if t.fieldname in self.noprint_fields:
                 continue
             if t.type == 'Field' and t.is_lengthfield:
                 continue
-            write('    // processing {}: {} {}\n'
-                  .format(o.name, t.fieldtype, t.fieldname))
-
             write('    item = cJSON_GetObjectItem(o, "{}");\n'
                   .format(t.fieldname))
-            write('    if (!item) return 0;\n')
+            write('    if (!item) goto error;\n')
+            error += 1
             self._dispatch[t.type](self, t, toplevel=True)
             write('\n')
 
-        write('\n')
         write('    *len = l;\n')
         write('    return a;\n')
+
+        if error:
+            write('\n  error:\n')
+            write('    cJSON_free(a);\n')
+            write('    return 0;\n')
         write('}\n')
 
     def print_using(self, o):
@@ -570,7 +594,7 @@ class FromJSON():
             return
 
         t = o.using
-        write('static inline void *vl_api_{name}_t_fromjson (void *mp, '
+        write('static inline int vl_api_{name}_t_fromjson (void **mp, '
               'int *len, cJSON *o, vl_api_{name}_t *a) {{\n'
               .format(name=o.name))
         if 'length' in o.alias:
@@ -583,7 +607,7 @@ class FromJSON():
             write('    vl_api_{t}_fromjson(o, ({t} *)a);\n'
                   .format(t=t.fieldtype))
 
-        write('    return mp;\n')
+        write('    return 0;\n')
         write('}\n')
 
     _dispatch['Typedef'] = print_typedef
@@ -662,7 +686,7 @@ TOP_BOILERPLATE = '''\
     || defined(vl_printfun) ||defined(vl_endianfun) \\
     || defined(vl_api_version)||defined(vl_typedefs) \\
     || defined(vl_msg_name)||defined(vl_msg_name_crc_list) \\
-    || defined(vl_api_version_tuple)
+    || defined(vl_api_version_tuple) || defined(vl_calcsizefun)
 /* ok, something was selected */
 #else
 #warning no content included from {input_filename}
@@ -727,7 +751,7 @@ def msg_name_crc_list(s, suffix):
 
     for t in s['Define']:
         output += "\\\n_(VL_API_%s, %s, %08x) " % \
-                   (t.name.upper(), t.name, t.crc)
+            (t.name.upper(), t.name, t.crc)
     output += "\n#endif"
 
     return output
@@ -913,10 +937,13 @@ def printfun(objs, stream, modulename):
 #define _uword_cast long
 #endif
 
+#include "{module}.api_tojson.h"
+#include "{module}.api_fromjson.h"
+
 '''
 
     signature = '''\
-static inline void *vl_api_{name}_t_print (vl_api_{name}_t *a, void *handle)
+static inline void *vl_api_{name}_t_print{suffix} (vl_api_{name}_t *a, void *handle)
 {{
     u8 *s = 0;
     u32 indent __attribute__((unused)) = 2;
@@ -931,7 +958,7 @@ static inline void *vl_api_{name}_t_print (vl_api_{name}_t *a, void *handle)
         if t.manual_print:
             write("/***** manual: vl_api_%s_t_print  *****/\n\n" % t.name)
             continue
-        write(signature.format(name=t.name))
+        write(signature.format(name=t.name, suffix=''))
         write('    /* Message definition: vl_api_{}_t: */\n'.format(t.name))
         write("    s = format(s, \"vl_api_%s_t:\");\n" % t.name)
         for o in t.block:
@@ -942,6 +969,16 @@ static inline void *vl_api_{name}_t_print (vl_api_{name}_t *a, void *handle)
         write('    return handle;\n')
         write('}\n\n')
 
+        write(signature.format(name=t.name, suffix='_json'))
+        write('    cJSON * o = vl_api_{}_t_tojson(a);\n'.format(t.name))
+        write('    (void)s;\n')
+        write('    char *out = cJSON_Print(o);\n')
+        write('    vl_print(handle, out);\n')
+        write('    cJSON_Delete(o);\n')
+        write('    cJSON_free(out);\n')
+        write('    return handle;\n')
+        write('}\n\n')
+
     write("\n#endif")
     write("\n#endif /* vl_printfun */\n")
 
@@ -1039,7 +1076,7 @@ def endianfun_array(o):
 '''
 
     output = ''
-    if o.fieldtype == 'u8' or o.fieldtype == 'string':
+    if o.fieldtype == 'u8' or o.fieldtype == 'string' or o.fieldtype == 'bool':
         output += '    /* a->{n} = a->{n} (no-op) */\n'.format(n=o.fieldname)
     else:
         lfield = 'a->' + o.lengthfield if o.lengthfield else o.length
@@ -1109,7 +1146,7 @@ static inline void vl_api_{name}_t_endian (vl_api_{name}_t *a)
 '''
 
     for t in objs:
-        if t.__class__.__name__ == 'Enum' or t.__class__.__name__ == 'EnumFlag' :
+        if t.__class__.__name__ == 'Enum' or t.__class__.__name__ == 'EnumFlag':
             output += signature.format(name=t.name)
             if t.enumtype in ENDIAN_STRINGS:
                 output += ('    *a = {}(*a);\n'
@@ -1151,6 +1188,78 @@ static inline void vl_api_{name}_t_endian (vl_api_{name}_t *a)
     return output
 
 
+def calc_size_fun(objs, modulename):
+    '''Main entry point for calculate size function generation'''
+    output = '''\
+
+/****** Calculate size functions *****/\n\
+#ifdef vl_calcsizefun
+#ifndef included_{module}_calcsizefun
+#define included_{module}_calcsizefun
+
+'''
+    output = output.format(module=modulename)
+
+    signature = '''\
+/* calculate message size of message in network byte order */
+static inline uword vl_api_{name}_t_calc_size (vl_api_{name}_t *a)
+{{
+'''
+
+    for o in objs:
+        tname = o.__class__.__name__
+
+        output += signature.format(name=o.name)
+        output += f"      return sizeof(*a)"
+        if tname == 'Using':
+            if 'length' in o.alias:
+                try:
+                    tmp = int(o.alias['length'])
+                    if tmp == 0:
+                        raise (f"Unexpected length '0' for alias {o}")
+                except:
+                    # output += f" + vl_api_{o.alias.name}_t_calc_size({o.name})"
+                    print("culprit:")
+                    print(o)
+                    print(dir(o.alias))
+                    print(o.alias)
+                    raise
+        elif tname == 'Enum' or tname == 'EnumFlag':
+            pass
+        else:
+            for b in o.block:
+                if b.type == 'Option':
+                    continue
+                elif b.type == 'Field':
+                    if b.fieldtype.startswith('vl_api_'):
+                        output += f" - sizeof(a->{b.fieldname})"
+                        output += f" + {b.fieldtype}_calc_size(&a->{b.fieldname})"
+                elif b.type == 'Array':
+                    if b.lengthfield:
+                        m = list(filter(lambda x: x.fieldname == b.lengthfield, o.block))
+                        if len(m) != 1:
+                            raise Exception(f"Expected 1 match for field '{b.lengthfield}', got '{m}'")
+                        lf = m[0]
+                        if lf.fieldtype in ENDIAN_STRINGS:
+                            output += f" + {ENDIAN_STRINGS[lf.fieldtype]}(a->{b.lengthfield}) * sizeof(a->{b.fieldname}[0])"
+                        elif lf.fieldtype == "u8":
+                            output += f" + a->{b.lengthfield} * sizeof(a->{b.fieldname}[0])"
+                        else:
+                            raise Exception(f"Don't know how to endian swap {lf.fieldtype}")
+                    else:
+                        # Fixed length strings decay to nul terminated u8
+                        if b.fieldtype == 'string':
+                            if b.modern_vla:
+                                output += f" + vl_api_string_len(&a->{b.fieldname})"
+
+        output += ";\n"
+        output += '}\n\n'
+    output += "\n#endif"
+    output += "\n#endif /* vl_calcsizefun */\n\n"
+
+    return output
+
+
 def version_tuple(s, module):
     '''Generate semantic version string'''
     output = '''\
@@ -1195,7 +1304,7 @@ def generate_include_counters(s, stream):
         write('   {}_N_ERROR\n'.format(csetname.upper()))
         write('}} vl_counter_{}_enum_t;\n'.format(csetname))
 
-        write('extern vl_counter_t {}_error_counters[];\n'.format(csetname))
+        write('extern vlib_error_desc_t {}_error_counters[];\n'.format(csetname))
 
 
 def generate_include_types(s, module, stream):
@@ -1221,7 +1330,7 @@ def generate_include_types(s, module, stream):
             filename = i.filename.replace('plugins/', '')
             write('#include <{}_types.h>\n'.format(filename))
 
-    for o in s['types'] + s['Define']:
+    for o in itertools.chain(s['types'], s['Define']):
         tname = o.__class__.__name__
         if tname == 'Using':
             if 'length' in o.alias:
@@ -1280,6 +1389,7 @@ def generate_include_types(s, module, stream):
                                      .format(b, o.name))
 
             write('} vl_api_%s_t;\n' % o.name)
+            write(f'#define VL_API_{o.name.upper()}_IS_CONSTANT_SIZE ({0 if o.vla else 1})\n\n')
 
     for t in s['Define']:
         write('#define VL_API_{ID}_CRC "{n}_{crc:08x}"\n'
@@ -1299,6 +1409,10 @@ def generate_c_boilerplate(services, defines, counters, file_crc,
 #include "{module}.api.h"
 #undef vl_endianfun
 
+#define vl_calcsizefun
+#include "{module}.api.h"
+#undef vl_calsizefun
+
 /* instantiate all the print functions we know about */
 #define vl_print(handle, ...) vlib_cli_output (handle, __VA_ARGS__)
 #define vl_printfun
@@ -1329,8 +1443,15 @@ def generate_c_boilerplate(services, defines, counters, file_crc,
               '   .cleanup = vl_noop_handler,\n'
               '   .endian = vl_api_{n}_t_endian,\n'
               '   .print = vl_api_{n}_t_print,\n'
-              '   .is_autoendian = 0}};\n'
-              .format(n=s.caller, ID=s.caller.upper()))
+              '   .traced = 1,\n'
+              '   .replay = 1,\n'
+              '   .print_json = vl_api_{n}_t_print_json,\n'
+              '   .tojson = vl_api_{n}_t_tojson,\n'
+              '   .fromjson = vl_api_{n}_t_fromjson,\n'
+              '   .calc_size = vl_api_{n}_t_calc_size,\n'
+              '   .is_autoendian = {auto}}};\n'
+              .format(n=s.caller, ID=s.caller.upper(),
+                      auto=d.autoendian))
         write('   vl_msg_api_config (&c);\n')
         try:
             d = define_hash[s.reply]
@@ -1341,8 +1462,15 @@ def generate_c_boilerplate(services, defines, counters, file_crc,
                   '  .cleanup = vl_noop_handler,\n'
                   '  .endian = vl_api_{n}_t_endian,\n'
                   '  .print = vl_api_{n}_t_print,\n'
-                  '  .is_autoendian = 0}};\n'
-                  .format(n=s.reply, ID=s.reply.upper()))
+                  '  .traced = 1,\n'
+                  '  .replay = 1,\n'
+                  '  .print_json = vl_api_{n}_t_print_json,\n'
+                  '  .tojson = vl_api_{n}_t_tojson,\n'
+                  '  .fromjson = vl_api_{n}_t_fromjson,\n'
+                  '  .calc_size = vl_api_{n}_t_calc_size,\n'
+                  '  .is_autoendian = {auto}}};\n'
+                  .format(n=s.reply, ID=s.reply.upper(),
+                          auto=d.autoendian))
             write('   vl_msg_api_config (&c);\n')
         except KeyError:
             pass
@@ -1356,7 +1484,7 @@ def generate_c_boilerplate(services, defines, counters, file_crc,
 
     for cnt in counters:
         csetname = cnt.name
-        write('vl_counter_t {}_error_counters[] = {{\n'.format(csetname))
+        write('vlib_error_desc_t {}_error_counters[] = {{\n'.format(csetname))
         for c in cnt.block:
             write('  {\n')
             write('   .name = "{}",\n'.format(c['name']))
@@ -1378,6 +1506,10 @@ def generate_c_test_boilerplate(services, defines, file_crc, module, plugin,
 #include "{module}.api.h"
 #undef vl_endianfun
 
+#define vl_calcsizefun
+#include "{module}.api.h"
+#undef vl_calsizefun
+
 /* instantiate all the print functions we know about */
 #define vl_print(handle, ...) vlib_cli_output (handle, __VA_ARGS__)
 #define vl_printfun
@@ -1436,7 +1568,11 @@ def generate_c_test_boilerplate(services, defines, file_crc, module, plugin,
               '                           vl_noop_handler,\n'
               '                           vl_api_{n}_t_endian, '
               '                           vl_api_{n}_t_print,\n'
-              '                           sizeof(vl_api_{n}_t), 1);\n'
+              '                           sizeof(vl_api_{n}_t), 1,\n'
+              '                           vl_api_{n}_t_print_json,\n'
+              '                           vl_api_{n}_t_tojson,\n'
+              '                           vl_api_{n}_t_fromjson,\n'
+              '                           vl_api_{n}_t_calc_size);\n'
               .format(n=s.reply, ID=s.reply.upper()))
         write('   hash_set_mem (vam->function_by_name, "{n}", api_{n});\n'
               .format(n=s.caller))
@@ -1455,15 +1591,15 @@ def generate_c_test_boilerplate(services, defines, file_crc, module, plugin,
                   '                           vl_noop_handler,\n'
                   '                           vl_api_{n}_t_endian, '
                   '                           vl_api_{n}_t_print,\n'
-                  '                           sizeof(vl_api_{n}_t), 1);\n'
+                  '                           sizeof(vl_api_{n}_t), 1,\n'
+                  '                           vl_api_{n}_t_print_json,\n'
+                  '                           vl_api_{n}_t_tojson,\n'
+                  '                           vl_api_{n}_t_fromjson,\n'
+                  '                           vl_api_{n}_t_calc_size);\n'
                   .format(n=e, ID=e.upper()))
 
     write('}\n')
-    if plugin:
-        write('clib_error_t * vat_plugin_register (vat_main_t *vam)\n')
-    else:
-        write('clib_error_t * vat_{}_plugin_register (vat_main_t *vam)\n'
-              .format(module))
+    write('clib_error_t * vat_plugin_register (vat_main_t *vam)\n')
     write('{\n')
     write('   {n}_test_main_t * mainp = &{n}_test_main;\n'.format(n=module))
     write('   mainp->vat_main = vam;\n')
@@ -1511,12 +1647,13 @@ api_{n} (cJSON *o)
   mp->_vl_msg_id = vac_get_msg_index(VL_API_{N}_CRC);
   vl_api_{n}_t_endian(mp);
   vac_write((char *)mp, len);
-  free(mp);
+  cJSON_free(mp);
 
   /* Read reply */
   char *p;
   int l;
   vac_read(&p, &l, 5); // XXX: Fix timeout
+  if (p == 0 || l == 0) return 0;
     // XXX Will fail in case of event received. Do loop
   if (ntohs(*((u16 *)p)) != vac_get_msg_index(VL_API_{R}_CRC)) {{
     fprintf(stderr, "Mismatched reply\\n");
@@ -1543,7 +1680,7 @@ api_{n} (cJSON *o)
   mp->_vl_msg_id = msg_id;
   vl_api_{n}_t_endian(mp);
   vac_write((char *)mp, len);
-  free(mp);
+  cJSON_free(mp);
 
   vat2_control_ping(123); // FIX CONTEXT
   cJSON *reply = cJSON_CreateArray();
@@ -1556,6 +1693,10 @@ api_{n} (cJSON *o)
     char *p;
     int l;
     vac_read(&p, &l, 5); // XXX: Fix timeout
+    if (p == 0 || l == 0) {{
+      cJSON_free(reply);
+      return 0;
+    }}
 
     /* Message can be one of [_details, control_ping_reply
      * or unrelated event]
@@ -1566,6 +1707,10 @@ api_{n} (cJSON *o)
     }}
 
     if (reply_msg_id == details_msg_id) {{
+        if (l < sizeof(vl_api_{r}_t)) {{
+            cJSON_free(reply);
+            return 0;
+        }}
         vl_api_{r}_t *rmp = (vl_api_{r}_t *)p;
         vl_api_{r}_t_endian(rmp);
         cJSON_AddItemToArray(reply, vl_api_{r}_t_tojson(rmp));
@@ -1591,7 +1736,7 @@ api_{n} (cJSON *o)
 
   vl_api_{n}_t_endian(mp);
   vac_write((char *)mp, len);
-  free(mp);
+  cJSON_free(mp);
 
   cJSON *reply = cJSON_CreateArray();
 
@@ -1657,7 +1802,9 @@ def generate_c_test2_boilerplate(services, defines, module, stream):
 #include <vnet/ethernet/ethernet_format_fns.h>
 
 #define vl_typedefs             /* define message structures */
-#include <vpp/api/vpe_all_api_h.h>
+#include <vlibmemory/vl_memory_api_h.h>
+#include <vlibmemory/vlib.api_types.h>
+#include <vlibmemory/vlib.api.h>
 #undef vl_typedefs
 
 #include "{module}.api_enum.h"
@@ -1667,6 +1814,10 @@ def generate_c_test2_boilerplate(services, defines, module, stream):
 #include "{module}.api.h"
 #undef vl_endianfun
 
+#define vl_calcsizefun
+#include "{module}.api.h"
+#undef vl_calsizefun
+
 #define vl_print(handle, ...) vlib_cli_output (handle, __VA_ARGS__)
 #define vl_printfun
 #include "{module}.api.h"
@@ -1687,13 +1838,16 @@ def generate_c_test2_boilerplate(services, defines, module, stream):
             continue
         c_test_api_service(s, s.stream, stream)
 
-    write('void vat2_register_function(char *, cJSON * (*)(cJSON *));\n')
+    write('void vat2_register_function(char *, cJSON * (*)(cJSON *), cJSON * (*)(void *), u32);\n')
     # write('__attribute__((constructor))')
     write('clib_error_t *\n')
     write('vat2_register_plugin (void) {\n')
     for s in services:
-        write('   vat2_register_function("{n}", api_{n});\n'
-              .format(n=s.caller))
+        if s.reply not in define_hash:
+            continue
+        crc = define_hash[s.caller].crc
+        write('   vat2_register_function("{n}", api_{n}, (cJSON * (*)(void *))vl_api_{n}_t_tojson, 0x{crc:08x});\n'
+              .format(n=s.caller, crc=crc))
     write('   return 0;\n')
     write('}\n')
 
@@ -1798,6 +1952,7 @@ def run(args, apifilename, s):
     output += stream.getvalue()
     stream.close()
     output += endianfun(s['types'] + s['Define'], modulename)
+    output += calc_size_fun(s['types'] + s['Define'], modulename)
     output += version_tuple(s, basename)
     output += BOTTOM_BOILERPLATE.format(input_filename=basename,
                                         file_crc=s['file_crc'])