Luajit API and some examples
[vpp.git] / vpp-api / lua / vpp-lapi.lua
1 --[[
2 /*
3  * Copyright (c) 2016 Cisco and/or its affiliates.
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at:
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 ]]
17
18 local vpp = {}
19
20 local ffi = require("ffi")
21
22 --[[
23
24 The basic type definitions. A bit of weird gymnastic with
25 unionization of the hton* and ntoh* functions results
26 is to make handling of signed and unsigned types a bit cleaner,
27 essentially building typecasting into a C union.
28
29 The vl_api_opaque_message_t is a synthetic type assumed to have
30 enough storage to hold the entire API message regardless of the type.
31 During the operation it is casted to the specific message struct types.
32
33 ]]
34
35
36 ffi.cdef([[
37
38 typedef uint8_t u8;
39 typedef int8_t i8;
40 typedef uint16_t u16;
41 typedef int16_t i16;
42 typedef uint32_t u32;
43 typedef int32_t i32;
44 typedef uint64_t u64;
45 typedef int64_t i64;
46 typedef double f64;
47 typedef float f32;
48
49 #pragma pack(1)
50 typedef union {
51   u16 u16;
52   i16 i16;
53 } lua_ui16t;
54
55 #pragma pack(1)
56 typedef union {
57   u32 u32;
58   i32 i32;
59 } lua_ui32t;
60
61 u16 ntohs(uint16_t hostshort);
62 u16 htons(uint16_t hostshort);
63 u32 htonl(uint32_t along);
64 u32 ntohl(uint32_t along);
65 void *memset(void *s, int c, size_t n);
66 void *memcpy(void *dest, void *src, size_t n);
67
68 #pragma pack(1)
69 typedef struct _vl_api_opaque_message {
70   u16 _vl_msg_id;
71   u8  data[65536];
72 } vl_api_opaque_message_t;
73 ]])
74
75
76 -- CRC-based version stuff
77
78 local crc32c_table = ffi.new('const uint32_t[256]',
79   { 0x00000000, 0xF26B8303, 0xE13B70F7, 0x1350F3F4,
80   0xC79A971F, 0x35F1141C, 0x26A1E7E8, 0xD4CA64EB,
81   0x8AD958CF, 0x78B2DBCC, 0x6BE22838, 0x9989AB3B,
82   0x4D43CFD0, 0xBF284CD3, 0xAC78BF27, 0x5E133C24,
83   0x105EC76F, 0xE235446C, 0xF165B798, 0x030E349B,
84   0xD7C45070, 0x25AFD373, 0x36FF2087, 0xC494A384,
85   0x9A879FA0, 0x68EC1CA3, 0x7BBCEF57, 0x89D76C54,
86   0x5D1D08BF, 0xAF768BBC, 0xBC267848, 0x4E4DFB4B,
87   0x20BD8EDE, 0xD2D60DDD, 0xC186FE29, 0x33ED7D2A,
88   0xE72719C1, 0x154C9AC2, 0x061C6936, 0xF477EA35,
89   0xAA64D611, 0x580F5512, 0x4B5FA6E6, 0xB93425E5,
90   0x6DFE410E, 0x9F95C20D, 0x8CC531F9, 0x7EAEB2FA,
91   0x30E349B1, 0xC288CAB2, 0xD1D83946, 0x23B3BA45,
92   0xF779DEAE, 0x05125DAD, 0x1642AE59, 0xE4292D5A,
93   0xBA3A117E, 0x4851927D, 0x5B016189, 0xA96AE28A,
94   0x7DA08661, 0x8FCB0562, 0x9C9BF696, 0x6EF07595,
95   0x417B1DBC, 0xB3109EBF, 0xA0406D4B, 0x522BEE48,
96   0x86E18AA3, 0x748A09A0, 0x67DAFA54, 0x95B17957,
97   0xCBA24573, 0x39C9C670, 0x2A993584, 0xD8F2B687,
98   0x0C38D26C, 0xFE53516F, 0xED03A29B, 0x1F682198,
99   0x5125DAD3, 0xA34E59D0, 0xB01EAA24, 0x42752927,
100   0x96BF4DCC, 0x64D4CECF, 0x77843D3B, 0x85EFBE38,
101   0xDBFC821C, 0x2997011F, 0x3AC7F2EB, 0xC8AC71E8,
102   0x1C661503, 0xEE0D9600, 0xFD5D65F4, 0x0F36E6F7,
103   0x61C69362, 0x93AD1061, 0x80FDE395, 0x72966096,
104   0xA65C047D, 0x5437877E, 0x4767748A, 0xB50CF789,
105   0xEB1FCBAD, 0x197448AE, 0x0A24BB5A, 0xF84F3859,
106   0x2C855CB2, 0xDEEEDFB1, 0xCDBE2C45, 0x3FD5AF46,
107   0x7198540D, 0x83F3D70E, 0x90A324FA, 0x62C8A7F9,
108   0xB602C312, 0x44694011, 0x5739B3E5, 0xA55230E6,
109   0xFB410CC2, 0x092A8FC1, 0x1A7A7C35, 0xE811FF36,
110   0x3CDB9BDD, 0xCEB018DE, 0xDDE0EB2A, 0x2F8B6829,
111   0x82F63B78, 0x709DB87B, 0x63CD4B8F, 0x91A6C88C,
112   0x456CAC67, 0xB7072F64, 0xA457DC90, 0x563C5F93,
113   0x082F63B7, 0xFA44E0B4, 0xE9141340, 0x1B7F9043,
114   0xCFB5F4A8, 0x3DDE77AB, 0x2E8E845F, 0xDCE5075C,
115   0x92A8FC17, 0x60C37F14, 0x73938CE0, 0x81F80FE3,
116   0x55326B08, 0xA759E80B, 0xB4091BFF, 0x466298FC,
117   0x1871A4D8, 0xEA1A27DB, 0xF94AD42F, 0x0B21572C,
118   0xDFEB33C7, 0x2D80B0C4, 0x3ED04330, 0xCCBBC033,
119   0xA24BB5A6, 0x502036A5, 0x4370C551, 0xB11B4652,
120   0x65D122B9, 0x97BAA1BA, 0x84EA524E, 0x7681D14D,
121   0x2892ED69, 0xDAF96E6A, 0xC9A99D9E, 0x3BC21E9D,
122   0xEF087A76, 0x1D63F975, 0x0E330A81, 0xFC588982,
123   0xB21572C9, 0x407EF1CA, 0x532E023E, 0xA145813D,
124   0x758FE5D6, 0x87E466D5, 0x94B49521, 0x66DF1622,
125   0x38CC2A06, 0xCAA7A905, 0xD9F75AF1, 0x2B9CD9F2,
126   0xFF56BD19, 0x0D3D3E1A, 0x1E6DCDEE, 0xEC064EED,
127   0xC38D26C4, 0x31E6A5C7, 0x22B65633, 0xD0DDD530,
128   0x0417B1DB, 0xF67C32D8, 0xE52CC12C, 0x1747422F,
129   0x49547E0B, 0xBB3FFD08, 0xA86F0EFC, 0x5A048DFF,
130   0x8ECEE914, 0x7CA56A17, 0x6FF599E3, 0x9D9E1AE0,
131   0xD3D3E1AB, 0x21B862A8, 0x32E8915C, 0xC083125F,
132   0x144976B4, 0xE622F5B7, 0xF5720643, 0x07198540,
133   0x590AB964, 0xAB613A67, 0xB831C993, 0x4A5A4A90,
134   0x9E902E7B, 0x6CFBAD78, 0x7FAB5E8C, 0x8DC0DD8F,
135   0xE330A81A, 0x115B2B19, 0x020BD8ED, 0xF0605BEE,
136   0x24AA3F05, 0xD6C1BC06, 0xC5914FF2, 0x37FACCF1,
137   0x69E9F0D5, 0x9B8273D6, 0x88D28022, 0x7AB90321,
138   0xAE7367CA, 0x5C18E4C9, 0x4F48173D, 0xBD23943E,
139   0xF36E6F75, 0x0105EC76, 0x12551F82, 0xE03E9C81,
140   0x34F4F86A, 0xC69F7B69, 0xD5CF889D, 0x27A40B9E,
141   0x79B737BA, 0x8BDCB4B9, 0x988C474D, 0x6AE7C44E,
142   0xBE2DA0A5, 0x4C4623A6, 0x5F16D052, 0xAD7D5351 }
143 );
144
145 local function CRC8(crc, d)
146   return bit.bxor(bit.rshift(crc, 8), crc32c_table[bit.band(0xff, bit.bxor(crc, d))])
147 end
148
149 local function CRC16(crc, d)
150   crc = CRC8(crc, bit.band(d, 0xFF))
151   d = bit.rshift(d, 8)
152   crc = CRC8(crc, bit.band(d, 0xFF))
153   return crc
154 end
155
156 local function string_crc(str, crc)
157   for i=1,#str do
158     -- print("S", i, string.byte(str, i), string.char(string.byte(str, i)))
159     crc = CRC8(crc, string.byte(str, i))
160   end
161   return crc
162 end
163
164 local tokens = {
165   { ["match"] =' ', ["act"]             = { }  },
166   { ["match"] ='\n', ["act"]             = { }  },
167   { ["match"] ="manual_endian", ["act"]  = { "NODE_MANUAL_ENDIAN", "MANUAL_ENDIAN",    276 } },
168   { ["match"] ="define", ["act"]         = { "NODE_DEFINE",        "DEFINE",           267 } },
169   { ["match"] ="dont_trace", ["act"]     = { "NODE_DONT_TRACE",    "DONT_TRACE",       279 } },
170   { ["match"] ="f64", ["act"]            = { "NODE_F64",           "PRIMTYPE",         string_crc } },
171   { ["match"] ="i16", ["act"]            = { "NODE_I16",           "PRIMTYPE",         string_crc } },
172   { ["match"] ="i32", ["act"]            = { "NODE_I32",           "PRIMTYPE",         string_crc } },
173   { ["match"] ="i64", ["act"]            = { "NODE_I64",           "PRIMTYPE",         string_crc } },
174   { ["match"] ="i8", ["act"]             = { "NODE_I8",            "PRIMTYPE",         string_crc } },
175   { ["match"] ="manual_print", ["act"]   = { "NODE_MANUAL_PRINT",  "MANUAL_PRINT",     275 } },
176   { ["match"] ="noversion", ["act"]      = { "NODE_NOVERSION",     "NOVERSION",        274 } },
177   { ["match"] ="packed", ["act"]         = { "NODE_PACKED",        "TPACKED",          266 } },
178   { ["match"] ="typeonly", ["act"]       = { "NODE_TYPEONLY",      "TYPEONLY",         278 } },
179   { ["match"] ="u16", ["act"]            = { "NODE_U16",           "PRIMTYPE",         string_crc } },
180   { ["match"] ="u32", ["act"]            = { "NODE_U32",           "PRIMTYPE",         string_crc } },
181   { ["match"] ="u64", ["act"]            = { "NODE_U64",           "PRIMTYPE",         string_crc } },
182   { ["match"] ="u8", ["act"]             = { "NODE_U8",            "PRIMTYPE",         string_crc } },
183   { ["match"] ="union", ["act"]          = { "NODE_UNION",         "UNION",            271 } },
184   { ["match"] ="uword", ["act"]          = { "NODE_UWORD",         "PRIMTYPE",         string_crc } },
185   { ["match"] ="%(", ["act"]             = { "NODE_LPAR",          "LPAR",             259 } },
186   { ["match"] ="%)", ["act"]             = { "NODE_RPAR",          "RPAR",             258 } },
187   { ["match"] =";", ["act"]              = { "NODE_SEMI",          "SEMI",             260 } },
188   { ["match"] ="%[", ["act"]             = { "NODE_LBRACK",        "LBRACK",           261 } },
189   { ["match"] ="%]", ["act"]             = { "NODE_RBRACK",        "RBRACK",           262 } },
190   { ["match"] ="%{", ["act"]             = { "NODE_LCURLY",        "LCURLY",           268 } },
191   { ["match"] ="%}", ["act"]             = { "NODE_RCURLY",        "RCURLY",           269 } },
192   { ["match"] ='%b""', ["act"]           = { "NODE_STRING",        "STRING",           string_crc } },
193   { ["match"] ='%b@@', ["act"]           = { "NODE_HELPER",        "HELPER_STRING",    string_crc } },
194   -- TODO: \ must be consumed
195   { ["match"] ='[_a-zA-Z][_a-zA-Z0-9]*',
196                        ["act"]           = { "NODE_NAME",          "NAME",             string_crc } },
197   { ["match"] ='[0-9]+', ["act"]         = { "NODE_NUMBER",        "NUMBER",           string_crc } },
198   { ["match"] ='#[^\n]+', ["act"]            = { "NODE_PRAGMA",        "PRAGMA",           nil } },
199 }
200
201
202 function vpp.crc_version_string(data)
203   local input_crc = 0
204   -- Get rid of comments
205   data = data:gsub("/%*.-%*/", "")
206   data = data:gsub("//[^\n]+", "")
207   -- print(data)
208   idx = 1
209   while (true) do
210     local matched = nil
211     for k, v in ipairs(tokens) do
212       if not matched then
213         local x, y, cap = string.find(data, v["match"], idx)
214         if x == idx then
215           matched = { ["node"] = v["act"], ["x"] = x, ["y"] = y, ["cap"] = cap, ["chars"] = string.sub(data, x, y)  }
216           -- print(k, v, x, y, cap, matched.chars, matched.node[0] )
217         end
218       end
219     end
220     if matched then
221       idx = idx + (matched.y - matched.x + 1)
222       if matched.node[1] then
223         local act = matched.node[3]
224         if type(act) == "function" then
225           input_crc = act(matched.chars, input_crc)
226         elseif type(act) == "number" then
227           input_crc = CRC16(input_crc, act)
228         end
229         -- print(vpp.dump(matched))
230       end
231     else
232       -- print("NOT MATCHED!")
233       local crc = CRC16(input_crc, 0xFFFFFFFF)
234       return string.sub(string.format("%x", crc), -8)
235     end
236   end
237 end
238
239
240 function vpp.dump(o)
241    if type(o) == 'table' then
242       local s = '{ '
243       for k,v in pairs(o) do
244          if type(k) ~= 'number' then k = '"'..k..'"' end
245          s = s .. '['..k..'] = ' .. vpp.dump(v) .. ','
246       end
247       return s .. '} '
248    else
249       return tostring(o)
250    end
251 end
252
253 function vpp.hex_dump(buf)
254   local ret = {}
255   for i=1,math.ceil(#buf/16) * 16 do
256     if (i-1) % 16 == 0 then table.insert(ret, string.format('%08X  ', i-1)) end
257     table.insert(ret, ( i > #buf and '   ' or string.format('%02X ', buf:byte(i)) ))
258     if i %  8 == 0 then table.insert(ret, ' ') end
259     if i % 16 == 0 then table.insert(ret, buf:sub(i-16+1, i):gsub('%c','.')..'\n' ) end
260   end
261   return table.concat(ret)
262 end
263
264
265 function vpp.c_str(text_in)
266   local text = text_in -- \000 will be helpfully added by ffi.copy
267   local c_str = ffi.new("char[?]", #text+1)
268   ffi.copy(c_str, text)
269   return c_str
270 end
271
272
273 function vpp.init(vpp, args)
274   local pneum_api = args.pneum_api or [[
275  int cough_pneum_attach(char *pneum_path, char *cough_path);
276  int pneum_connect(char *name, char *chroot_prefix);
277  int pneum_connect_sync(char *name, char *chroot_prefix);
278  int pneum_disconnect(void);
279  int pneum_read(char **data, int *l);
280  int pneum_write(char *data, int len);
281
282 void pneum_data_free(char *data);
283 ]]
284
285   vpp.pneum_path = args.pneum_path
286   ffi.cdef(pneum_api)
287   local init_res = 0
288   if pcall(function()
289                      vpp.cough_path = args.cough_path or "./libcough.so"
290                      vpp.cough = ffi.load(vpp.cough_path)
291            end) then
292     pcall(function()
293       if(vpp.cough.cough_pneum_attach) then
294         vpp.pneum_is_cough = true
295         print("libcough detected\n")
296         init_res = vpp.cough.cough_pneum_attach(vpp.c_str(vpp.pneum_path), vpp.c_str(vpp.cough_path))
297         vpp.pneum = vpp.cough
298       end
299     end)
300   else
301     vpp.pneum = ffi.load(vpp.pneum_path)
302   end
303   if (init_res < 0) then
304     return nil
305   end
306
307   vpp.next_msg_num = 1
308   vpp.msg_name_to_number = {}
309   vpp.msg_name_to_fields = {}
310   vpp.msg_number_to_name = {}
311   vpp.msg_number_to_type = {}
312   vpp.msg_number_to_pointer_type = {}
313   vpp.c_type_to_fields = {}
314   vpp.events = {}
315   vpp.plugin_version = {}
316   vpp.is_connected = false
317
318
319   vpp.t_lua2c = {}
320   vpp.t_c2lua = {}
321   vpp.t_lua2c["u8"] = function(c_type, src, dst_c_ptr)
322     if type(src) == "string" then
323       -- ffi.copy adds a zero byte at the end. Grrr.
324       -- ffi.copy(dst_c_ptr, src)
325       ffi.C.memcpy(dst_c_ptr, vpp.c_str(src), #src)
326       return(#src)
327     elseif type(src) == "table" then
328       for i,v in ipairs(src) do
329         ffi.cast("u8 *", dst_c_ptr)[i-1] = v
330       end
331       return(#src)
332     else
333       return 1, src -- ffi.cast("u8", src)
334     end
335   end
336   vpp.t_c2lua["u8"] = function(c_type, src_ptr, src_len)
337     if src_len then
338       return ffi.string(src_ptr, src_len)
339     else
340       return (tonumber(src_ptr))
341     end
342   end
343
344   vpp.t_lua2c["u16"] = function(c_type, src, dst_c_ptr)
345     if type(src) == "table" then
346       for i,v in ipairs(src) do
347         ffi.cast("u16 *", dst_c_ptr)[i-1] = ffi.C.htons(v)
348       end
349       return(2 * #src)
350     else
351       return 2, (ffi.C.htons(src))
352     end
353   end
354   vpp.t_c2lua["u16"] = function(c_type, src_ptr, src_len)
355     if src_len then
356       local out = {}
357       for i = 0,src_len-1 do
358         out[i+1] = tonumber(ffi.C.ntohs(src_ptr[i]))
359       end
360       return out
361     else
362       return (tonumber(ffi.C.ntohs(src_ptr)))
363     end
364   end
365
366   vpp.t_lua2c["u32"] = function(c_type, src, dst_c_ptr)
367     if type(src) == "table" then
368       for i,v in ipairs(src) do
369         ffi.cast("u32 *", dst_c_ptr)[i-1] = ffi.C.htonl(v)
370       end
371       return(4 * #src)
372     else
373       return 4, (ffi.C.htonl(src))
374     end
375   end
376   vpp.t_c2lua["u32"] = function(c_type, src_ptr, src_len)
377     if src_len then
378       local out = {}
379       for i = 0,src_len-1 do
380         out[i+1] = tonumber(ffi.C.ntohl(src_ptr[i]))
381       end
382       return out
383     else
384       return (tonumber(ffi.C.ntohl(src_ptr)))
385     end
386   end
387   vpp.t_lua2c["i32"] = function(c_type, src, dst_c_ptr)
388     if type(src) == "table" then
389       for i,v in ipairs(src) do
390         ffi.cast("i32 *", dst_c_ptr)[i-1] = ffi.C.htonl(v)
391       end
392       return(4 * #src)
393     else
394       return 4, (ffi.C.htonl(src))
395     end
396   end
397   vpp.t_c2lua["i32"] = function(c_type, src_ptr, src_len)
398     local ntohl = function(src)
399       local u32val = ffi.cast("u32", src)
400       local ntohlval = (ffi.C.ntohl(u32val))
401       local out = tonumber(ffi.cast("i32", ntohlval + 0LL))
402       return out
403     end
404     if src_len then
405       local out = {}
406       for i = 0,src_len-1 do
407         out[i+1] = tonumber(ntohl(src_ptr[i]))
408       end
409     else
410       return (tonumber(ntohl(src_ptr)))
411     end
412   end
413
414   vpp.t_lua2c["u64"] = function(c_type, src, dst_c_ptr)
415     if type(src) == "table" then
416       for i,v in ipairs(src) do
417         ffi.cast("u64 *", dst_c_ptr)[i-1] = v --- FIXME ENDIAN
418       end
419       return(8 * #src)
420     else
421       return 8, ffi.cast("u64", src) --- FIXME ENDIAN
422     end
423   end
424   vpp.t_c2lua["u64"] = function(c_type, src_ptr, src_len)
425     if src_len then
426       local out = {}
427       for i = 0,src_len-1 do
428         out[i+1] = tonumber(src_ptr[i]) -- FIXME ENDIAN
429       end
430       return out
431     else
432       return (tonumber(src_ptr)) --FIXME ENDIAN
433     end
434   end
435
436
437
438
439   vpp.t_lua2c["__MSG__"] = function(c_type, src, dst_c_ptr)
440     local dst = ffi.cast(c_type .. " *", dst_c_ptr)
441     local additional_len = 0
442     local fields_info = vpp.c_type_to_fields[c_type]
443     -- print("__MSG__ type: " .. tostring(c_type))
444     ffi.C.memset(dst_c_ptr, 0, ffi.sizeof(dst[0]))
445     -- print(vpp.dump(fields_info))
446     -- print(vpp.dump(src))
447     for k,v in pairs(src) do
448       local field = fields_info[k]
449       if not field then
450         print("ERROR: field " .. tostring(k) .. " in message " .. tostring(c_type) .. " is unknown")
451       end
452       local lua2c = vpp.t_lua2c[field.c_type]
453       -- print("__MSG__ field " .. tostring(k) .. " : " .. vpp.dump(field))
454       -- if the field is not an array type, try to coerce the argument to a number
455       if not field.array and type(v) == "string" then
456         v = tonumber(v)
457       end
458       if not lua2c then
459         print("__MSG__ " .. tostring(c_type) .. " t_lua2c: can not store field " .. field.name ..
460               " type " .. field.c_type .. " dst " .. tostring(dst[k]))
461         return 0
462       end
463       local len = 0
464       local val = nil
465       if field.array and (type(v) == "table") then
466         print("NTFY: field " .. tostring(k) .. " in message " .. tostring(c_type) .. " is an array")
467         for field_i, field_v in ipairs(v) do
468           print("NTFY: setting member#" .. tostring(field_i) .. " to value " .. vpp.dump(field_v))
469           local field_len, field_val = lua2c(field.c_type, field_v, dst[k][field_i-1])
470           len = len + field_len
471         end
472       else
473         len, val = lua2c(field.c_type, v, dst[k])
474       end
475       if not field.array then
476         dst[k] = val
477       else
478         if 0 == field.array then
479           additional_len = additional_len + len
480           -- print("Adding " .. tostring(len) .. " bytes due to field " .. tostring(field.name))
481           -- If there is a variable storing the length
482           -- and the input table does not set it, do magic
483           if field.array_size and not src[field.array_size] then
484             local size_field = fields_info[field.array_size]
485             if size_field then
486               dst[field.array_size] = vpp.t_c2lua[size_field.c_type](size_field.c_type, len)
487             end
488           end
489         end
490       end
491       -- print("Full message:\n" .. vpp.hex_dump(ffi.string(ffi.cast('void *', req_store_cache), 64)))
492     end
493     return (ffi.sizeof(dst[0])+additional_len)
494   end
495
496   vpp.t_c2lua["__MSG__"] = function(c_type, src_ptr, src_len)
497     local out = {}
498     local reply_typed_ptr = ffi.cast(c_type .. " *", src_ptr)
499     local field_desc = vpp.c_type_to_fields[c_type]
500     if src_len then
501       for i = 0,src_len-1 do
502         out[i+1] = vpp.t_c2lua[c_type](c_type, src_ptr[i])
503       end
504       return out
505     end
506
507     for k, v in pairs(field_desc) do
508       local v_c2lua = vpp.t_c2lua[v.c_type]
509       if v_c2lua then
510         local len = v.array
511         -- print(dump(v))
512         if len then
513           local len_field_name = k .. "_length"
514           local len_field = field_desc[len_field_name]
515           if (len_field) then
516             local real_len = vpp.t_c2lua[len_field.c_type](len_field.c_type, reply_typed_ptr[len_field_name])
517             out[k] =  v_c2lua(v.c_type, reply_typed_ptr[k], real_len)
518           elseif len == 0 then
519             -- check if len = 0, then must be a field which contains the size
520             len_field =  field_desc[v.array_size]
521             local real_len = vpp.t_c2lua[len_field.c_type](len_field.c_type, reply_typed_ptr[v.array_size])
522             -- print("REAL length: " .. vpp.dump(v) .. " : " .. tostring(real_len))
523             out[k] = v_c2lua(v.c_type, reply_typed_ptr[k], real_len)
524           else
525             -- alas, just stuff the entire array
526             out[k] = v_c2lua(v.c_type, reply_typed_ptr[k], len)
527           end
528         else
529           out[k] =  v_c2lua(v.c_type, reply_typed_ptr[k])
530         end
531       else
532         out[k] = "<no accessor function for type " .. tostring(v.c_type) .. ">"
533       end
534       -- print(k, out[k])
535     end
536     return out
537   end
538
539   return vpp
540 end
541
542 function vpp.connect(vpp, client_name)
543     local name = "lua_client"
544     if client_name then
545       name = client_name
546     end
547     local ret = vpp.pneum.pneum_connect_sync(vpp.c_str(client_name), nil)
548     if tonumber(ret) == 0 then
549       vpp.is_connected = true
550     end
551   end
552
553 function vpp.disconnect(vpp)
554     vpp.pneum.pneum_disconnect()
555   end
556
557 function vpp.consume_api(vpp, path, plugin_name)
558     -- print("Consuming the VPP api from "..path)
559     local ffii = {}
560     local f = io.open(path, "r")
561     if not f then
562       print("Could not open " .. path)
563       return nil
564     end
565     local data = f:read("*all")
566     -- Remove all C comments
567     data = data:gsub("/%*.-%*/", "")
568     if vpp.is_connected and not plugin_name then
569       print(path .. ": must specify plugin name!")
570       return
571     end
572     if plugin_name then
573       vpp.plugin_version[plugin_name] = vpp.crc_version_string(data)
574       local full_plugin_name = plugin_name .. "_" .. vpp.plugin_version[plugin_name]
575       local reply = vpp:api_call("get_first_msg_id", { name = full_plugin_name } )
576       vpp.next_msg_num = tonumber(reply[1].first_msg_id)
577       print("Plugin " .. full_plugin_name .. " first message is " .. tostring(vpp.next_msg_num))
578     end
579     -- print ("data len: ", #data)
580     data = data:gsub("\n(.-)(%S+)%s*{([^}]*)}", function (preamble, name, members)
581       local _, typeonly = preamble:gsub("typeonly", "")
582       local maybe_msg_id_field = { [0] = "u16 _vl_msg_id;", "" }
583       local onedef = "\n\n#pragma pack(1)\ntypedef struct _vl_api_"..name.. " {\n" ..
584            -- "   u16 _vl_msg_id;" ..
585            maybe_msg_id_field[typeonly] ..
586            members:gsub("%[[a-zA-Z_]+]", "[0]") ..
587            "} vl_api_" .. name .. "_t;"
588
589       local c_type = "vl_api_" .. name .. "_t"
590
591       local fields = {}
592       -- vpp.msg_name_to_fields[name] = fields
593       -- print("CTYPE " .. c_type)
594       vpp.c_type_to_fields[c_type] = fields
595       vpp.t_lua2c[c_type] = vpp.t_lua2c["__MSG__"]
596       vpp.t_c2lua[c_type] = vpp.t_c2lua["__MSG__"]
597       local mirec = { name = "_vl_msg_id", c_type = "u16", array = nil, array_size = nil }
598       if typeonly == 0 then
599         fields[mirec.name] = mirec
600       end
601
602       -- populate the field reflection table for the message
603       -- sets the various type information as well as the accessors for lua<->C conversion
604       members:gsub("(%S+)%s+(%S+);", function (fieldtype, fieldname)
605           local fieldcount = nil
606           local fieldcountvar = nil
607           -- data = data:gsub("%[[a-zA-Z_]+]", "[0]")
608           fieldname = fieldname:gsub("(%b[])", function(cnt)
609               fieldcount = tonumber(cnt:sub(2, -2));
610               if not fieldcount then
611                 fieldcount = 0
612                 fieldcountvar = cnt:sub(2, -2)
613               end
614               return ""
615             end)
616           local fieldrec = { name = fieldname, c_type = fieldtype, array = fieldcount, array_size = fieldcountvar }
617           if fieldcount then
618             if fieldtype == "u8" then
619               -- any array of bytes is treated as a string
620             elseif vpp.t_lua2c[fieldtype] then
621               -- print("Array of " .. fieldtype .. " is ok!")
622             else
623               print("Unknown array type: ", name,  " : " , fieldname, " : ", fieldtype, ":", fieldcount, ":", fieldcountvar)
624             end
625           end
626           fields[fieldname] = fieldrec
627         end)
628
629       -- print(dump(fields))
630
631       if typeonly == 0 then
632         local this_message_number = vpp.next_msg_num
633         vpp.next_msg_num = vpp.next_msg_num + 1
634         vpp.msg_name_to_number[name] = this_message_number
635         vpp.msg_number_to_name[this_message_number] = name
636         vpp.msg_number_to_type[this_message_number] = "vl_api_" .. name .. "_t"
637         vpp.msg_number_to_pointer_type[this_message_number] = vpp.msg_number_to_type[this_message_number] .. " *"
638         onedef = onedef .. "\n\n enum { vl_msg_" .. name .. " = " .. this_message_number .. " };\n\n"
639       end
640       table.insert(ffii, onedef);
641       return "";
642       end)
643     local cdef = table.concat(ffii)
644     -- print(cdef)
645     ffi.cdef(cdef)
646   end
647
648
649 function vpp.lua2c(vpp, c_type, src, dst_c_ptr)
650   -- returns the number of bytes written to memory pointed by dst
651   local lua2c = vpp.t_lua2c[c_type]
652   if lua2c then
653     return(lua2c(c_type, src, dst_c_ptr))
654   else
655     print("vpp.lua2c: do not know how to store type " .. c_type)
656     return 0
657   end
658 end
659
660 function vpp.c2lua(vpp, c_type, src_ptr, src_len)
661   -- returns the lua data structure
662   local c2lua = vpp.t_c2lua[c_type]
663   if c2lua then
664     return(c2lua(c_type, src_ptr, src_len))
665   else
666     print("vpp.c2lua: do not know how to load type " .. c_type)
667     return nil
668   end
669 end
670
671 local req_store_cache = ffi.new("vl_api_opaque_message_t[1]")
672
673 function vpp.api_write(vpp, api_name, req_table)
674     local msg_num = vpp.msg_name_to_number[api_name]
675     if not msg_num then
676       print ("API call "..api_name.." is not known")
677       return nil
678     end
679
680     if not req_table then
681       req_table = {}
682     end
683     req_table._vl_msg_id = msg_num
684
685     local packed_len = vpp:lua2c(vpp.msg_number_to_type[msg_num], req_table, req_store_cache)
686     if vpp.debug_dump then
687       print("Write Message length: " .. tostring(packed_len) .. "\n" .. vpp.hex_dump(ffi.string(ffi.cast('void *', req_store_cache), packed_len)))
688     end
689
690     res = vpp.pneum.pneum_write(ffi.cast('void *', req_store_cache), packed_len)
691     return res
692   end
693
694 local rep_store_cache = ffi.new("vl_api_opaque_message_t *[1]")
695 local rep_len_cache = ffi.new("int[1]")
696
697 function vpp.api_read(vpp)
698     local rep_type = "vl_api_opaque_message_t"
699     local rep = rep_store_cache
700     local replen = rep_len_cache
701     res = vpp.pneum.pneum_read(ffi.cast("void *", rep), replen)
702     if vpp.debug_dump then
703       print("Read Message length: " .. tostring(replen[0]) .. "\n" .. vpp.hex_dump(ffi.string(ffi.cast('void *', rep[0]), replen[0])))
704     end
705
706     local reply_msg_num = ffi.C.ntohs(rep[0]._vl_msg_id)
707     local reply_msg_name = vpp.msg_number_to_name[reply_msg_num]
708
709     local reply_typed_ptr = ffi.cast(vpp.msg_number_to_pointer_type[reply_msg_num], rep[0])
710     local out = vpp:c2lua(vpp.msg_number_to_type[reply_msg_num], rep[0], nil, replen[0])
711     if type(out) == "table" then
712       out["luaapi_message_name"] = reply_msg_name
713     end
714
715     vpp.pneum.pneum_data_free(ffi.cast('void *',rep[0]))
716
717     return reply_msg_name, out
718   end
719
720 function vpp.api_call(vpp, api_name, req_table, options_in)
721     local msg_num = vpp.msg_name_to_number[api_name]
722     local end_message_name = api_name .."_reply"
723     local replies = {}
724     local cstruct = ""
725     local options = options_in or {}
726     if msg_num then
727       vpp:api_write(api_name, req_table)
728       if not vpp.msg_name_to_number[end_message_name] or options.force_ping then
729         end_message_name = "control_ping_reply"
730         vpp:api_write("control_ping")
731       end
732       repeat
733         reply_message_name, reply = vpp:api_read()
734         if reply and not reply.context then
735           -- there may be async events inbetween
736           table.insert(vpp.events, reply)
737         else
738           if reply_message_name ~= "control_ping_reply" then
739             -- do not insert the control ping encapsulation
740             table.insert(replies, reply)
741           end
742         end
743         -- print(reply)
744       until reply_message_name == end_message_name
745     else
746       print(api_name .. " is an unknown API call")
747       return nil
748     end
749     return replies
750   end
751
752 return vpp