c88b456d260dc7e58b02bef5e3a6801704ff9c10
[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, void *cb);
277  int pneum_disconnect(void);
278  int pneum_read(char **data, int *l);
279  int pneum_write(char *data, int len);
280  void pneum_free(char *data);
281 ]]
282
283   vpp.pneum_path = args.pneum_path
284   ffi.cdef(pneum_api)
285   local init_res = 0
286   vpp.pneum = ffi.load(vpp.pneum_path)
287   if (init_res < 0) then
288     return nil
289   end
290
291   vpp.next_msg_num = 1
292   vpp.msg_name_to_number = {}
293   vpp.msg_name_to_fields = {}
294   vpp.msg_number_to_name = {}
295   vpp.msg_number_to_type = {}
296   vpp.msg_number_to_pointer_type = {}
297   vpp.c_type_to_fields = {}
298   vpp.events = {}
299   vpp.plugin_version = {}
300   vpp.is_connected = false
301
302
303   vpp.t_lua2c = {}
304   vpp.t_c2lua = {}
305   vpp.t_lua2c["u8"] = function(c_type, src, dst_c_ptr)
306     if type(src) == "string" then
307       -- ffi.copy adds a zero byte at the end. Grrr.
308       -- ffi.copy(dst_c_ptr, src)
309       ffi.C.memcpy(dst_c_ptr, vpp.c_str(src), #src)
310       return(#src)
311     elseif type(src) == "table" then
312       for i,v in ipairs(src) do
313         ffi.cast("u8 *", dst_c_ptr)[i-1] = v
314       end
315       return(#src)
316     else
317       return 1, src -- ffi.cast("u8", src)
318     end
319   end
320   vpp.t_c2lua["u8"] = function(c_type, src_ptr, src_len)
321     if src_len then
322       return ffi.string(src_ptr, src_len)
323     else
324       return (tonumber(src_ptr))
325     end
326   end
327
328   vpp.t_lua2c["u16"] = function(c_type, src, dst_c_ptr)
329     if type(src) == "table" then
330       for i,v in ipairs(src) do
331         ffi.cast("u16 *", dst_c_ptr)[i-1] = ffi.C.htons(v)
332       end
333       return(2 * #src)
334     else
335       return 2, (ffi.C.htons(src))
336     end
337   end
338   vpp.t_c2lua["u16"] = function(c_type, src_ptr, src_len)
339     if src_len then
340       local out = {}
341       for i = 0,src_len-1 do
342         out[i+1] = tonumber(ffi.C.ntohs(src_ptr[i]))
343       end
344       return out
345     else
346       return (tonumber(ffi.C.ntohs(src_ptr)))
347     end
348   end
349
350   vpp.t_lua2c["u32"] = function(c_type, src, dst_c_ptr)
351     if type(src) == "table" then
352       for i,v in ipairs(src) do
353         ffi.cast("u32 *", dst_c_ptr)[i-1] = ffi.C.htonl(v)
354       end
355       return(4 * #src)
356     else
357       return 4, (ffi.C.htonl(src))
358     end
359   end
360   vpp.t_c2lua["u32"] = function(c_type, src_ptr, src_len)
361     if src_len then
362       local out = {}
363       for i = 0,src_len-1 do
364         out[i+1] = tonumber(ffi.C.ntohl(src_ptr[i]))
365       end
366       return out
367     else
368       return (tonumber(ffi.C.ntohl(src_ptr)))
369     end
370   end
371   vpp.t_lua2c["i32"] = function(c_type, src, dst_c_ptr)
372     if type(src) == "table" then
373       for i,v in ipairs(src) do
374         ffi.cast("i32 *", dst_c_ptr)[i-1] = ffi.C.htonl(v)
375       end
376       return(4 * #src)
377     else
378       return 4, (ffi.C.htonl(src))
379     end
380   end
381   vpp.t_c2lua["i32"] = function(c_type, src_ptr, src_len)
382     local ntohl = function(src)
383       local u32val = ffi.cast("u32", src)
384       local ntohlval = (ffi.C.ntohl(u32val))
385       local out = tonumber(ffi.cast("i32", ntohlval + 0LL))
386       return out
387     end
388     if src_len then
389       local out = {}
390       for i = 0,src_len-1 do
391         out[i+1] = tonumber(ntohl(src_ptr[i]))
392       end
393     else
394       return (tonumber(ntohl(src_ptr)))
395     end
396   end
397
398   vpp.t_lua2c["u64"] = function(c_type, src, dst_c_ptr)
399     if type(src) == "table" then
400       for i,v in ipairs(src) do
401         ffi.cast("u64 *", dst_c_ptr)[i-1] = v --- FIXME ENDIAN
402       end
403       return(8 * #src)
404     else
405       return 8, ffi.cast("u64", src) --- FIXME ENDIAN
406     end
407   end
408   vpp.t_c2lua["u64"] = function(c_type, src_ptr, src_len)
409     if src_len then
410       local out = {}
411       for i = 0,src_len-1 do
412         out[i+1] = tonumber(src_ptr[i]) -- FIXME ENDIAN
413       end
414       return out
415     else
416       return (tonumber(src_ptr)) --FIXME ENDIAN
417     end
418   end
419
420
421
422
423   vpp.t_lua2c["__MSG__"] = function(c_type, src, dst_c_ptr)
424     local dst = ffi.cast(c_type .. " *", dst_c_ptr)
425     local additional_len = 0
426     local fields_info = vpp.c_type_to_fields[c_type]
427     -- print("__MSG__ type: " .. tostring(c_type))
428     ffi.C.memset(dst_c_ptr, 0, ffi.sizeof(dst[0]))
429     -- print(vpp.dump(fields_info))
430     -- print(vpp.dump(src))
431     for k,v in pairs(src) do
432       local field = fields_info[k]
433       if not field then
434         print("ERROR: field " .. tostring(k) .. " in message " .. tostring(c_type) .. " is unknown")
435       end
436       local lua2c = vpp.t_lua2c[field.c_type]
437       -- print("__MSG__ field " .. tostring(k) .. " : " .. vpp.dump(field))
438       -- if the field is not an array type, try to coerce the argument to a number
439       if not field.array and type(v) == "string" then
440         v = tonumber(v)
441       end
442       if not lua2c then
443         print("__MSG__ " .. tostring(c_type) .. " t_lua2c: can not store field " .. field.name ..
444               " type " .. field.c_type .. " dst " .. tostring(dst[k]))
445         return 0
446       end
447       local len = 0
448       local val = nil
449       if field.array and (type(v) == "table") then
450         -- print("NTFY: field " .. tostring(k) .. " in message " .. tostring(c_type) .. " is an array")
451         for field_i, field_v in ipairs(v) do
452           -- print("NTFY: setting member#" .. tostring(field_i) .. " to value " .. vpp.dump(field_v))
453           local field_len, field_val = lua2c(field.c_type, field_v, dst[k][field_i-1])
454           len = len + field_len
455         end
456       else
457         len, val = lua2c(field.c_type, v, dst[k])
458       end
459       if not field.array then
460         dst[k] = val
461       else
462         if 0 == field.array then
463           additional_len = additional_len + len
464           -- print("Adding " .. tostring(len) .. " bytes due to field " .. tostring(field.name))
465           -- If there is a variable storing the length
466           -- and the input table does not set it, do magic
467           if field.array_size and not src[field.array_size] then
468             local size_field = fields_info[field.array_size]
469             if size_field then
470               dst[field.array_size] = vpp.t_c2lua[size_field.c_type](size_field.c_type, len)
471             end
472           end
473         end
474       end
475       -- print("Full message:\n" .. vpp.hex_dump(ffi.string(ffi.cast('void *', req_store_cache), 64)))
476     end
477     return (ffi.sizeof(dst[0])+additional_len)
478   end
479
480   vpp.t_c2lua["__MSG__"] = function(c_type, src_ptr, src_len)
481     local out = {}
482     local reply_typed_ptr = ffi.cast(c_type .. " *", src_ptr)
483     local field_desc = vpp.c_type_to_fields[c_type]
484     if src_len then
485       for i = 0,src_len-1 do
486         out[i+1] = vpp.t_c2lua[c_type](c_type, src_ptr[i])
487       end
488       return out
489     end
490
491     for k, v in pairs(field_desc) do
492       local v_c2lua = vpp.t_c2lua[v.c_type]
493       if v_c2lua then
494         local len = v.array
495         -- print(dump(v))
496         if len then
497           local len_field_name = k .. "_length"
498           local len_field = field_desc[len_field_name]
499           if (len_field) then
500             local real_len = vpp.t_c2lua[len_field.c_type](len_field.c_type, reply_typed_ptr[len_field_name])
501             out[k] =  v_c2lua(v.c_type, reply_typed_ptr[k], real_len)
502           elseif len == 0 then
503             -- check if len = 0, then must be a field which contains the size
504             len_field =  field_desc[v.array_size]
505             local real_len = vpp.t_c2lua[len_field.c_type](len_field.c_type, reply_typed_ptr[v.array_size])
506             -- print("REAL length: " .. vpp.dump(v) .. " : " .. tostring(real_len))
507             out[k] = v_c2lua(v.c_type, reply_typed_ptr[k], real_len)
508           else
509             -- alas, just stuff the entire array
510             out[k] = v_c2lua(v.c_type, reply_typed_ptr[k], len)
511           end
512         else
513           out[k] =  v_c2lua(v.c_type, reply_typed_ptr[k])
514         end
515       else
516         out[k] = "<no accessor function for type " .. tostring(v.c_type) .. ">"
517       end
518       -- print(k, out[k])
519     end
520     return out
521   end
522
523   return vpp
524 end
525
526 function vpp.connect(vpp, client_name)
527     local name = "lua_client"
528     if client_name then
529       name = client_name
530     end
531     local ret = vpp.pneum.pneum_connect(vpp.c_str(client_name), nil, nil)
532     if tonumber(ret) == 0 then
533       vpp.is_connected = true
534     end
535   end
536
537 function vpp.disconnect(vpp)
538     vpp.pneum.pneum_disconnect()
539   end
540
541 function vpp.consume_api(vpp, path, plugin_name)
542     -- print("Consuming the VPP api from "..path)
543     local ffii = {}
544     local f = io.open(path, "r")
545     if not f then
546       print("Could not open " .. path)
547       return nil
548     end
549     local data = f:read("*all")
550     -- Remove all C comments
551     data = data:gsub("/%*.-%*/", "")
552     if vpp.is_connected and not plugin_name then
553       print(path .. ": must specify plugin name!")
554       return
555     end
556     if plugin_name then
557       vpp.plugin_version[plugin_name] = vpp.crc_version_string(data)
558       local full_plugin_name = plugin_name .. "_" .. vpp.plugin_version[plugin_name]
559       local reply = vpp:api_call("get_first_msg_id", { name = full_plugin_name } )
560       vpp.next_msg_num = tonumber(reply[1].first_msg_id)
561       print("Plugin " .. full_plugin_name .. " first message is " .. tostring(vpp.next_msg_num))
562     end
563     -- print ("data len: ", #data)
564     data = data:gsub("\n(.-)(%S+)%s*{([^}]*)}", function (preamble, name, members)
565       local _, typeonly = preamble:gsub("typeonly", "")
566       local maybe_msg_id_field = { [0] = "u16 _vl_msg_id;", "" }
567       local onedef = "\n\n#pragma pack(1)\ntypedef struct _vl_api_"..name.. " {\n" ..
568            -- "   u16 _vl_msg_id;" ..
569            maybe_msg_id_field[typeonly] ..
570            members:gsub("%[[a-zA-Z_]+]", "[0]") ..
571            "} vl_api_" .. name .. "_t;"
572
573       local c_type = "vl_api_" .. name .. "_t"
574
575       local fields = {}
576       -- vpp.msg_name_to_fields[name] = fields
577       -- print("CTYPE " .. c_type)
578       vpp.c_type_to_fields[c_type] = fields
579       vpp.t_lua2c[c_type] = vpp.t_lua2c["__MSG__"]
580       vpp.t_c2lua[c_type] = vpp.t_c2lua["__MSG__"]
581       local mirec = { name = "_vl_msg_id", c_type = "u16", array = nil, array_size = nil }
582       if typeonly == 0 then
583         fields[mirec.name] = mirec
584       end
585
586       -- populate the field reflection table for the message
587       -- sets the various type information as well as the accessors for lua<->C conversion
588       members:gsub("(%S+)%s+(%S+);", function (fieldtype, fieldname)
589           local fieldcount = nil
590           local fieldcountvar = nil
591           -- data = data:gsub("%[[a-zA-Z_]+]", "[0]")
592           fieldname = fieldname:gsub("(%b[])", function(cnt)
593               fieldcount = tonumber(cnt:sub(2, -2));
594               if not fieldcount then
595                 fieldcount = 0
596                 fieldcountvar = cnt:sub(2, -2)
597               end
598               return ""
599             end)
600           local fieldrec = { name = fieldname, c_type = fieldtype, array = fieldcount, array_size = fieldcountvar }
601           if fieldcount then
602             if fieldtype == "u8" then
603               -- any array of bytes is treated as a string
604             elseif vpp.t_lua2c[fieldtype] then
605               -- print("Array of " .. fieldtype .. " is ok!")
606             else
607               print("Unknown array type: ", name,  " : " , fieldname, " : ", fieldtype, ":", fieldcount, ":", fieldcountvar)
608             end
609           end
610           fields[fieldname] = fieldrec
611         end)
612
613       -- print(dump(fields))
614
615       if typeonly == 0 then
616         local this_message_number = vpp.next_msg_num
617         vpp.next_msg_num = vpp.next_msg_num + 1
618         vpp.msg_name_to_number[name] = this_message_number
619         vpp.msg_number_to_name[this_message_number] = name
620         vpp.msg_number_to_type[this_message_number] = "vl_api_" .. name .. "_t"
621         vpp.msg_number_to_pointer_type[this_message_number] = vpp.msg_number_to_type[this_message_number] .. " *"
622         onedef = onedef .. "\n\n enum { vl_msg_" .. name .. " = " .. this_message_number .. " };\n\n"
623       end
624       table.insert(ffii, onedef);
625       return "";
626       end)
627     local cdef = table.concat(ffii)
628     -- print(cdef)
629     ffi.cdef(cdef)
630   end
631
632
633 function vpp.lua2c(vpp, c_type, src, dst_c_ptr)
634   -- returns the number of bytes written to memory pointed by dst
635   local lua2c = vpp.t_lua2c[c_type]
636   if lua2c then
637     return(lua2c(c_type, src, dst_c_ptr))
638   else
639     print("vpp.lua2c: do not know how to store type " .. c_type)
640     return 0
641   end
642 end
643
644 function vpp.c2lua(vpp, c_type, src_ptr, src_len)
645   -- returns the lua data structure
646   local c2lua = vpp.t_c2lua[c_type]
647   if c2lua then
648     return(c2lua(c_type, src_ptr, src_len))
649   else
650     print("vpp.c2lua: do not know how to load type " .. c_type)
651     return nil
652   end
653 end
654
655 local req_store_cache = ffi.new("vl_api_opaque_message_t[1]")
656
657 function vpp.api_write(vpp, api_name, req_table)
658     local msg_num = vpp.msg_name_to_number[api_name]
659     if not msg_num then
660       print ("API call "..api_name.." is not known")
661       return nil
662     end
663
664     if not req_table then
665       req_table = {}
666     end
667     req_table._vl_msg_id = msg_num
668
669     local packed_len = vpp:lua2c(vpp.msg_number_to_type[msg_num], req_table, req_store_cache)
670     if vpp.debug_dump then
671       print("Write Message length: " .. tostring(packed_len) .. "\n" .. vpp.hex_dump(ffi.string(ffi.cast('void *', req_store_cache), packed_len)))
672     end
673
674     res = vpp.pneum.pneum_write(ffi.cast('void *', req_store_cache), packed_len)
675     return res
676   end
677
678 local rep_store_cache = ffi.new("vl_api_opaque_message_t *[1]")
679 local rep_len_cache = ffi.new("int[1]")
680
681 function vpp.api_read(vpp)
682     local rep_type = "vl_api_opaque_message_t"
683     local rep = rep_store_cache
684     local replen = rep_len_cache
685     res = vpp.pneum.pneum_read(ffi.cast("void *", rep), replen)
686     if vpp.debug_dump then
687       print("Read Message length: " .. tostring(replen[0]) .. "\n" .. vpp.hex_dump(ffi.string(ffi.cast('void *', rep[0]), replen[0])))
688     end
689
690     local reply_msg_num = ffi.C.ntohs(rep[0]._vl_msg_id)
691     local reply_msg_name = vpp.msg_number_to_name[reply_msg_num]
692
693     local reply_typed_ptr = ffi.cast(vpp.msg_number_to_pointer_type[reply_msg_num], rep[0])
694     local out = vpp:c2lua(vpp.msg_number_to_type[reply_msg_num], rep[0], nil, replen[0])
695     if type(out) == "table" then
696       out["luaapi_message_name"] = reply_msg_name
697     end
698
699     vpp.pneum.pneum_free(ffi.cast('void *',rep[0]))
700
701     return reply_msg_name, out
702   end
703
704 function vpp.api_call(vpp, api_name, req_table, options_in)
705     local msg_num = vpp.msg_name_to_number[api_name]
706     local end_message_name = api_name .."_reply"
707     local replies = {}
708     local cstruct = ""
709     local options = options_in or {}
710     if msg_num then
711       vpp:api_write(api_name, req_table)
712       if not vpp.msg_name_to_number[end_message_name] or options.force_ping then
713         end_message_name = "control_ping_reply"
714         vpp:api_write("control_ping")
715       end
716       repeat
717         reply_message_name, reply = vpp:api_read()
718         if reply and not reply.context then
719           -- there may be async events inbetween
720           table.insert(vpp.events, reply)
721         else
722           if reply_message_name ~= "control_ping_reply" then
723             -- do not insert the control ping encapsulation
724             table.insert(replies, reply)
725           end
726         end
727         -- print(reply)
728       until reply_message_name == end_message_name
729     else
730       print(api_name .. " is an unknown API call")
731       return nil
732     end
733     return replies
734   end
735
736 return vpp