blob: 4f2e3f53d593f5c23b178ea290fc93d1f6833488 [file] [log] [blame]
Andrew Yourtchenkofa1456a2016-11-11 16:32:52 +00001--[[
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
18local vpp = {}
19
20local ffi = require("ffi")
21
22--[[
23
24The basic type definitions. A bit of weird gymnastic with
25unionization of the hton* and ntoh* functions results
26is to make handling of signed and unsigned types a bit cleaner,
27essentially building typecasting into a C union.
28
29The vl_api_opaque_message_t is a synthetic type assumed to have
30enough storage to hold the entire API message regardless of the type.
31During the operation it is casted to the specific message struct types.
32
33]]
34
35
36ffi.cdef([[
37
38typedef uint8_t u8;
39typedef int8_t i8;
40typedef uint16_t u16;
41typedef int16_t i16;
42typedef uint32_t u32;
43typedef int32_t i32;
44typedef uint64_t u64;
45typedef int64_t i64;
46typedef double f64;
47typedef float f32;
48
49#pragma pack(1)
50typedef union {
51 u16 u16;
52 i16 i16;
53} lua_ui16t;
54
55#pragma pack(1)
56typedef union {
57 u32 u32;
58 i32 i32;
59} lua_ui32t;
60
61u16 ntohs(uint16_t hostshort);
62u16 htons(uint16_t hostshort);
63u32 htonl(uint32_t along);
64u32 ntohl(uint32_t along);
65void *memset(void *s, int c, size_t n);
66void *memcpy(void *dest, void *src, size_t n);
67
68#pragma pack(1)
69typedef 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
78local 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
145local function CRC8(crc, d)
146 return bit.bxor(bit.rshift(crc, 8), crc32c_table[bit.band(0xff, bit.bxor(crc, d))])
147end
148
149local 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
154end
155
156local 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
162end
163
164local 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
202function 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
237end
238
239
240function 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
251end
252
253function 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)
262end
263
264
265function 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
270end
271
272
273function 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
282void 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
540end
541
542function 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
553function vpp.disconnect(vpp)
554 vpp.pneum.pneum_disconnect()
555 end
556
557function 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
649function 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
658end
659
660function 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
669end
670
671local req_store_cache = ffi.new("vl_api_opaque_message_t[1]")
672
673function 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
694local rep_store_cache = ffi.new("vl_api_opaque_message_t *[1]")
695local rep_len_cache = ffi.new("int[1]")
696
697function 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
720function 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
752return vpp