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:
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
18 -- json decode/encode from https://gist.github.com/tylerneylon/59f4bcf316be525b30ab
19 -- licensed by the author tylerneylon into public domain. Thanks!
23 -- Internal functions.
25 local function kind_of(obj)
26 if type(obj) ~= 'table' then return type(obj) end
28 for _ in pairs(obj) do
29 if obj[i] ~= nil then i = i + 1 else return 'table' end
31 if i == 1 then return 'table' else return 'array' end
34 local function escape_str(s)
35 local in_char = {'\\', '"', '/', '\b', '\f', '\n', '\r', '\t'}
36 local out_char = {'\\', '"', '/', 'b', 'f', 'n', 'r', 't'}
37 for i, c in ipairs(in_char) do
38 s = s:gsub(c, '\\' .. out_char[i])
43 -- Returns pos, did_find; there are two cases:
44 -- 1. Delimiter found: pos = pos after leading space + delim; did_find = true.
45 -- 2. Delimiter not found: pos = pos after leading space; did_find = false.
46 -- This throws an error if err_if_missing is true and the delim is not found.
47 local function skip_delim(str, pos, delim, err_if_missing)
48 pos = pos + #str:match('^%s*', pos)
49 if str:sub(pos, pos) ~= delim then
50 if err_if_missing then
51 error('Expected ' .. delim .. ' near position ' .. pos)
58 -- Expects the given pos to be the first character after the opening quote.
59 -- Returns val, pos; the returned pos is after the closing quote character.
60 local function parse_str_val(str, pos, val)
62 local early_end_error = 'End of input found while parsing string.'
63 if pos > #str then error(early_end_error) end
64 local c = str:sub(pos, pos)
65 if c == '"' then return val, pos + 1 end
66 if c ~= '\\' then return parse_str_val(str, pos + 1, val .. c) end
67 -- We must have a \ character.
68 local esc_map = {b = '\b', f = '\f', n = '\n', r = '\r', t = '\t'}
69 local nextc = str:sub(pos + 1, pos + 1)
70 if not nextc then error(early_end_error) end
71 return parse_str_val(str, pos + 2, val .. (esc_map[nextc] or nextc))
74 -- Returns val, pos; the returned pos is after the number's final character.
75 local function parse_num_val(str, pos)
76 local num_str = str:match('^-?%d+%.?%d*[eE]?[+-]?%d*', pos)
77 local val = tonumber(num_str)
78 if not val then error('Error parsing number at position ' .. pos .. '.') end
79 return val, pos + #num_str
83 -- Public values and functions.
85 function json.stringify(obj, as_key)
86 local s = {} -- We'll build the string as an array of strings to be concatenated.
87 local kind = kind_of(obj) -- This is 'array' if it's an array or type(obj) otherwise.
88 if kind == 'array' then
89 if as_key then error('Can\'t encode array as key.') end
91 for i, val in ipairs(obj) do
92 if i > 1 then s[#s + 1] = ', ' end
93 s[#s + 1] = json.stringify(val)
96 elseif kind == 'table' then
97 if as_key then error('Can\'t encode table as key.') end
99 for k, v in pairs(obj) do
100 if #s > 1 then s[#s + 1] = ', ' end
101 s[#s + 1] = json.stringify(k, true)
103 s[#s + 1] = json.stringify(v)
106 elseif kind == 'string' then
107 return '"' .. escape_str(obj) .. '"'
108 elseif kind == 'number' then
109 if as_key then return '"' .. tostring(obj) .. '"' end
111 elseif kind == 'boolean' then
113 elseif kind == 'nil' then
116 error('Unjsonifiable type: ' .. kind .. '.')
118 return table.concat(s)
121 json.null = {} -- This is a one-off table to represent the null value.
123 function json.parse(str, pos, end_delim)
125 if pos > #str then error('Reached unexpected end of input.') end
126 local pos = pos + #str:match('^%s*', pos) -- Skip whitespace.
127 local first = str:sub(pos, pos)
128 if first == '{' then -- Parse an object.
129 local obj, key, delim_found = {}, true, true
132 key, pos = json.parse(str, pos, '}')
133 if key == nil then return obj, pos end
134 if not delim_found then error('Comma missing between object items.') end
135 pos = skip_delim(str, pos, ':', true) -- true -> error if missing.
136 obj[key], pos = json.parse(str, pos)
137 pos, delim_found = skip_delim(str, pos, ',')
139 elseif first == '[' then -- Parse an array.
140 local arr, val, delim_found = {}, true, true
143 val, pos = json.parse(str, pos, ']')
144 if val == nil then return arr, pos end
145 if not delim_found then error('Comma missing between array items.') end
147 pos, delim_found = skip_delim(str, pos, ',')
149 elseif first == '"' then -- Parse a string.
150 return parse_str_val(str, pos + 1)
151 elseif first == '-' or first:match('%d') then -- Parse a number.
152 return parse_num_val(str, pos)
153 elseif first == end_delim then -- End of an object or array.
155 else -- Parse true, false, or null.
156 local literals = {['true'] = true, ['false'] = false, ['null'] = json.null}
157 for lit_str, lit_val in pairs(literals) do
158 local lit_end = pos + #lit_str - 1
159 if str:sub(pos, lit_end) == lit_str then return lit_val, lit_end + 1 end
161 local pos_info_str = 'position ' .. pos .. ': ' .. str:sub(pos, pos + 10)
162 error('Invalid json syntax starting at ' .. pos_info_str)
169 local ffi = require("ffi")
173 The basic type definitions. A bit of weird gymnastic with
174 unionization of the hton* and ntoh* functions results
175 is to make handling of signed and unsigned types a bit cleaner,
176 essentially building typecasting into a C union.
178 The vl_api_opaque_message_t is a synthetic type assumed to have
179 enough storage to hold the entire API message regardless of the type.
180 During the operation it is casted to the specific message struct types.
189 typedef uint16_t u16;
191 typedef uint32_t u32;
193 typedef uint64_t u64;
210 u16 ntohs(uint16_t hostshort);
211 u16 htons(uint16_t hostshort);
212 u32 htonl(uint32_t along);
213 u32 ntohl(uint32_t along);
214 void *memset(void *s, int c, size_t n);
215 void *memcpy(void *dest, void *src, size_t n);
218 typedef struct _vl_api_opaque_message {
221 } vl_api_opaque_message_t;
225 -- CRC-based version stuff
227 local crc32c_table = ffi.new('const uint32_t[256]',
228 { 0x00000000, 0xF26B8303, 0xE13B70F7, 0x1350F3F4,
229 0xC79A971F, 0x35F1141C, 0x26A1E7E8, 0xD4CA64EB,
230 0x8AD958CF, 0x78B2DBCC, 0x6BE22838, 0x9989AB3B,
231 0x4D43CFD0, 0xBF284CD3, 0xAC78BF27, 0x5E133C24,
232 0x105EC76F, 0xE235446C, 0xF165B798, 0x030E349B,
233 0xD7C45070, 0x25AFD373, 0x36FF2087, 0xC494A384,
234 0x9A879FA0, 0x68EC1CA3, 0x7BBCEF57, 0x89D76C54,
235 0x5D1D08BF, 0xAF768BBC, 0xBC267848, 0x4E4DFB4B,
236 0x20BD8EDE, 0xD2D60DDD, 0xC186FE29, 0x33ED7D2A,
237 0xE72719C1, 0x154C9AC2, 0x061C6936, 0xF477EA35,
238 0xAA64D611, 0x580F5512, 0x4B5FA6E6, 0xB93425E5,
239 0x6DFE410E, 0x9F95C20D, 0x8CC531F9, 0x7EAEB2FA,
240 0x30E349B1, 0xC288CAB2, 0xD1D83946, 0x23B3BA45,
241 0xF779DEAE, 0x05125DAD, 0x1642AE59, 0xE4292D5A,
242 0xBA3A117E, 0x4851927D, 0x5B016189, 0xA96AE28A,
243 0x7DA08661, 0x8FCB0562, 0x9C9BF696, 0x6EF07595,
244 0x417B1DBC, 0xB3109EBF, 0xA0406D4B, 0x522BEE48,
245 0x86E18AA3, 0x748A09A0, 0x67DAFA54, 0x95B17957,
246 0xCBA24573, 0x39C9C670, 0x2A993584, 0xD8F2B687,
247 0x0C38D26C, 0xFE53516F, 0xED03A29B, 0x1F682198,
248 0x5125DAD3, 0xA34E59D0, 0xB01EAA24, 0x42752927,
249 0x96BF4DCC, 0x64D4CECF, 0x77843D3B, 0x85EFBE38,
250 0xDBFC821C, 0x2997011F, 0x3AC7F2EB, 0xC8AC71E8,
251 0x1C661503, 0xEE0D9600, 0xFD5D65F4, 0x0F36E6F7,
252 0x61C69362, 0x93AD1061, 0x80FDE395, 0x72966096,
253 0xA65C047D, 0x5437877E, 0x4767748A, 0xB50CF789,
254 0xEB1FCBAD, 0x197448AE, 0x0A24BB5A, 0xF84F3859,
255 0x2C855CB2, 0xDEEEDFB1, 0xCDBE2C45, 0x3FD5AF46,
256 0x7198540D, 0x83F3D70E, 0x90A324FA, 0x62C8A7F9,
257 0xB602C312, 0x44694011, 0x5739B3E5, 0xA55230E6,
258 0xFB410CC2, 0x092A8FC1, 0x1A7A7C35, 0xE811FF36,
259 0x3CDB9BDD, 0xCEB018DE, 0xDDE0EB2A, 0x2F8B6829,
260 0x82F63B78, 0x709DB87B, 0x63CD4B8F, 0x91A6C88C,
261 0x456CAC67, 0xB7072F64, 0xA457DC90, 0x563C5F93,
262 0x082F63B7, 0xFA44E0B4, 0xE9141340, 0x1B7F9043,
263 0xCFB5F4A8, 0x3DDE77AB, 0x2E8E845F, 0xDCE5075C,
264 0x92A8FC17, 0x60C37F14, 0x73938CE0, 0x81F80FE3,
265 0x55326B08, 0xA759E80B, 0xB4091BFF, 0x466298FC,
266 0x1871A4D8, 0xEA1A27DB, 0xF94AD42F, 0x0B21572C,
267 0xDFEB33C7, 0x2D80B0C4, 0x3ED04330, 0xCCBBC033,
268 0xA24BB5A6, 0x502036A5, 0x4370C551, 0xB11B4652,
269 0x65D122B9, 0x97BAA1BA, 0x84EA524E, 0x7681D14D,
270 0x2892ED69, 0xDAF96E6A, 0xC9A99D9E, 0x3BC21E9D,
271 0xEF087A76, 0x1D63F975, 0x0E330A81, 0xFC588982,
272 0xB21572C9, 0x407EF1CA, 0x532E023E, 0xA145813D,
273 0x758FE5D6, 0x87E466D5, 0x94B49521, 0x66DF1622,
274 0x38CC2A06, 0xCAA7A905, 0xD9F75AF1, 0x2B9CD9F2,
275 0xFF56BD19, 0x0D3D3E1A, 0x1E6DCDEE, 0xEC064EED,
276 0xC38D26C4, 0x31E6A5C7, 0x22B65633, 0xD0DDD530,
277 0x0417B1DB, 0xF67C32D8, 0xE52CC12C, 0x1747422F,
278 0x49547E0B, 0xBB3FFD08, 0xA86F0EFC, 0x5A048DFF,
279 0x8ECEE914, 0x7CA56A17, 0x6FF599E3, 0x9D9E1AE0,
280 0xD3D3E1AB, 0x21B862A8, 0x32E8915C, 0xC083125F,
281 0x144976B4, 0xE622F5B7, 0xF5720643, 0x07198540,
282 0x590AB964, 0xAB613A67, 0xB831C993, 0x4A5A4A90,
283 0x9E902E7B, 0x6CFBAD78, 0x7FAB5E8C, 0x8DC0DD8F,
284 0xE330A81A, 0x115B2B19, 0x020BD8ED, 0xF0605BEE,
285 0x24AA3F05, 0xD6C1BC06, 0xC5914FF2, 0x37FACCF1,
286 0x69E9F0D5, 0x9B8273D6, 0x88D28022, 0x7AB90321,
287 0xAE7367CA, 0x5C18E4C9, 0x4F48173D, 0xBD23943E,
288 0xF36E6F75, 0x0105EC76, 0x12551F82, 0xE03E9C81,
289 0x34F4F86A, 0xC69F7B69, 0xD5CF889D, 0x27A40B9E,
290 0x79B737BA, 0x8BDCB4B9, 0x988C474D, 0x6AE7C44E,
291 0xBE2DA0A5, 0x4C4623A6, 0x5F16D052, 0xAD7D5351 }
294 local function CRC8(crc, d)
295 return bit.bxor(bit.rshift(crc, 8), crc32c_table[bit.band(0xff, bit.bxor(crc, d))])
298 local function CRC16(crc, d)
299 crc = CRC8(crc, bit.band(d, 0xFF))
301 crc = CRC8(crc, bit.band(d, 0xFF))
305 local function string_crc(str, crc)
307 -- print("S", i, string.byte(str, i), string.char(string.byte(str, i)))
308 crc = CRC8(crc, string.byte(str, i))
314 { ["match"] =' ', ["act"] = { } },
315 { ["match"] ='\n', ["act"] = { } },
316 { ["match"] ="manual_endian", ["act"] = { "NODE_MANUAL_ENDIAN", "MANUAL_ENDIAN", 276 } },
317 { ["match"] ="define", ["act"] = { "NODE_DEFINE", "DEFINE", 267 } },
318 { ["match"] ="dont_trace", ["act"] = { "NODE_DONT_TRACE", "DONT_TRACE", 279 } },
319 { ["match"] ="f64", ["act"] = { "NODE_F64", "PRIMTYPE", string_crc } },
320 { ["match"] ="i16", ["act"] = { "NODE_I16", "PRIMTYPE", string_crc } },
321 { ["match"] ="i32", ["act"] = { "NODE_I32", "PRIMTYPE", string_crc } },
322 { ["match"] ="i64", ["act"] = { "NODE_I64", "PRIMTYPE", string_crc } },
323 { ["match"] ="i8", ["act"] = { "NODE_I8", "PRIMTYPE", string_crc } },
324 { ["match"] ="manual_print", ["act"] = { "NODE_MANUAL_PRINT", "MANUAL_PRINT", 275 } },
325 { ["match"] ="noversion", ["act"] = { "NODE_NOVERSION", "NOVERSION", 274 } },
326 { ["match"] ="packed", ["act"] = { "NODE_PACKED", "TPACKED", 266 } },
327 { ["match"] ="typeonly", ["act"] = { "NODE_TYPEONLY", "TYPEONLY", 278 } },
328 { ["match"] ="u16", ["act"] = { "NODE_U16", "PRIMTYPE", string_crc } },
329 { ["match"] ="u32", ["act"] = { "NODE_U32", "PRIMTYPE", string_crc } },
330 { ["match"] ="u64", ["act"] = { "NODE_U64", "PRIMTYPE", string_crc } },
331 { ["match"] ="u8", ["act"] = { "NODE_U8", "PRIMTYPE", string_crc } },
332 { ["match"] ="union", ["act"] = { "NODE_UNION", "UNION", 271 } },
333 { ["match"] ="uword", ["act"] = { "NODE_UWORD", "PRIMTYPE", string_crc } },
334 { ["match"] ="%(", ["act"] = { "NODE_LPAR", "LPAR", 259 } },
335 { ["match"] ="%)", ["act"] = { "NODE_RPAR", "RPAR", 258 } },
336 { ["match"] =";", ["act"] = { "NODE_SEMI", "SEMI", 260 } },
337 { ["match"] ="%[", ["act"] = { "NODE_LBRACK", "LBRACK", 261 } },
338 { ["match"] ="%]", ["act"] = { "NODE_RBRACK", "RBRACK", 262 } },
339 { ["match"] ="%{", ["act"] = { "NODE_LCURLY", "LCURLY", 268 } },
340 { ["match"] ="%}", ["act"] = { "NODE_RCURLY", "RCURLY", 269 } },
341 { ["match"] ='%b""', ["act"] = { "NODE_STRING", "STRING", string_crc } },
342 { ["match"] ='%b@@', ["act"] = { "NODE_HELPER", "HELPER_STRING", string_crc } },
343 -- TODO: \ must be consumed
344 { ["match"] ='[_a-zA-Z][_a-zA-Z0-9]*',
345 ["act"] = { "NODE_NAME", "NAME", string_crc } },
346 { ["match"] ='[0-9]+', ["act"] = { "NODE_NUMBER", "NUMBER", string_crc } },
347 { ["match"] ='#[^\n]+', ["act"] = { "NODE_PRAGMA", "PRAGMA", nil } },
351 function vpp.crc_version_string(data)
353 -- Get rid of comments
354 data = data:gsub("/%*.-%*/", "")
355 data = data:gsub("//[^\n]+", "")
360 for k, v in ipairs(tokens) do
362 local x, y, cap = string.find(data, v["match"], idx)
364 matched = { ["node"] = v["act"], ["x"] = x, ["y"] = y, ["cap"] = cap, ["chars"] = string.sub(data, x, y) }
365 -- print(k, v, x, y, cap, matched.chars, matched.node[0] )
370 idx = idx + (matched.y - matched.x + 1)
371 if matched.node[1] then
372 local act = matched.node[3]
373 if type(act) == "function" then
374 input_crc = act(matched.chars, input_crc)
375 elseif type(act) == "number" then
376 input_crc = CRC16(input_crc, act)
378 -- print(vpp.dump(matched))
381 -- print("NOT MATCHED!")
382 local crc = CRC16(input_crc, 0xFFFFFFFF)
383 return string.sub(string.format("%x", crc), -8)
390 if type(o) == 'table' then
392 for k,v in pairs(o) do
393 if type(k) ~= 'number' then k = '"'..k..'"' end
394 s = s .. '['..k..'] = ' .. vpp.dump(v) .. ','
402 function vpp.hex_dump(buf)
404 for i=1,math.ceil(#buf/16) * 16 do
405 if (i-1) % 16 == 0 then table.insert(ret, string.format('%08X ', i-1)) end
406 table.insert(ret, ( i > #buf and ' ' or string.format('%02X ', buf:byte(i)) ))
407 if i % 8 == 0 then table.insert(ret, ' ') end
408 if i % 16 == 0 then table.insert(ret, buf:sub(i-16+1, i):gsub('%c','.')..'\n' ) end
410 return table.concat(ret)
414 function vpp.c_str(text_in)
415 local text = text_in -- \000 will be helpfully added by ffi.copy
416 local c_str = ffi.new("char[?]", #text+1)
417 ffi.copy(c_str, text)
422 function vpp.init(vpp, args)
423 local vac_api = args.vac_api or [[
424 int cough_vac_attach(char *vac_path, char *cough_path);
425 int vac_connect(char *name, char *chroot_prefix, void *cb);
426 int vac_disconnect(void);
427 int vac_read(char **data, int *l);
428 int vac_write(char *data, int len);
429 void vac_free(char *data);
430 uint32_t vac_get_msg_index(unsigned char * name);
433 vpp.vac_path = args.vac_path
436 vpp.vac = ffi.load(vpp.vac_path)
437 if (init_res < 0) then
442 vpp.msg_name_to_number = {}
443 vpp.msg_name_to_fields = {}
444 vpp.msg_number_to_name = {}
445 vpp.msg_number_to_type = {}
446 vpp.msg_number_to_pointer_type = {}
447 vpp.msg_name_to_crc = {}
448 vpp.c_type_to_fields = {}
450 vpp.plugin_version = {}
451 vpp.is_connected = false
456 vpp.t_lua2c["u8"] = function(c_type, src, dst_c_ptr)
457 if type(src) == "string" then
458 -- ffi.copy adds a zero byte at the end. Grrr.
459 -- ffi.copy(dst_c_ptr, src)
460 ffi.C.memcpy(dst_c_ptr, vpp.c_str(src), #src)
462 elseif type(src) == "table" then
463 for i,v in ipairs(src) do
464 ffi.cast("u8 *", dst_c_ptr)[i-1] = v
468 return 1, src -- ffi.cast("u8", src)
471 vpp.t_c2lua["u8"] = function(c_type, src_ptr, src_len)
473 return ffi.string(src_ptr, src_len)
475 return (tonumber(src_ptr))
479 vpp.t_lua2c["u16"] = function(c_type, src, dst_c_ptr)
480 if type(src) == "table" then
481 for i,v in ipairs(src) do
482 ffi.cast("u16 *", dst_c_ptr)[i-1] = ffi.C.htons(v)
486 return 2, (ffi.C.htons(src))
489 vpp.t_c2lua["u16"] = function(c_type, src_ptr, src_len)
492 for i = 0,src_len-1 do
493 out[i+1] = tonumber(ffi.C.ntohs(src_ptr[i]))
497 return (tonumber(ffi.C.ntohs(src_ptr)))
501 vpp.t_lua2c["u32"] = function(c_type, src, dst_c_ptr)
502 if type(src) == "table" then
503 for i,v in ipairs(src) do
504 ffi.cast("u32 *", dst_c_ptr)[i-1] = ffi.C.htonl(v)
508 return 4, (ffi.C.htonl(src))
511 vpp.t_c2lua["u32"] = function(c_type, src_ptr, src_len)
514 for i = 0,src_len-1 do
515 out[i+1] = tonumber(ffi.C.ntohl(src_ptr[i]))
519 return (tonumber(ffi.C.ntohl(src_ptr)))
522 vpp.t_lua2c["i32"] = function(c_type, src, dst_c_ptr)
523 if type(src) == "table" then
524 for i,v in ipairs(src) do
525 ffi.cast("i32 *", dst_c_ptr)[i-1] = ffi.C.htonl(v)
529 return 4, (ffi.C.htonl(src))
532 vpp.t_c2lua["i32"] = function(c_type, src_ptr, src_len)
533 local ntohl = function(src)
534 local u32val = ffi.cast("u32", src)
535 local ntohlval = (ffi.C.ntohl(u32val))
536 local out = tonumber(ffi.cast("i32", ntohlval + 0LL))
541 for i = 0,src_len-1 do
542 out[i+1] = tonumber(ntohl(src_ptr[i]))
545 return (tonumber(ntohl(src_ptr)))
549 vpp.t_lua2c["u64"] = function(c_type, src, dst_c_ptr)
550 if type(src) == "table" then
551 for i,v in ipairs(src) do
552 ffi.cast("u64 *", dst_c_ptr)[i-1] = v --- FIXME ENDIAN
556 return 8, ffi.cast("u64", src) --- FIXME ENDIAN
559 vpp.t_c2lua["u64"] = function(c_type, src_ptr, src_len)
562 for i = 0,src_len-1 do
563 out[i+1] = tonumber(src_ptr[i]) -- FIXME ENDIAN
567 return (tonumber(src_ptr)) --FIXME ENDIAN
574 vpp.t_lua2c["__MSG__"] = function(c_type, src, dst_c_ptr)
575 local dst = ffi.cast(c_type .. " *", dst_c_ptr)
576 local additional_len = 0
577 local fields_info = vpp.c_type_to_fields[c_type]
578 -- print("__MSG__ type: " .. tostring(c_type))
579 ffi.C.memset(dst_c_ptr, 0, ffi.sizeof(dst[0]))
580 -- print(vpp.dump(fields_info))
581 -- print(vpp.dump(src))
582 for k,v in pairs(src) do
583 local field = fields_info[k]
585 print("ERROR: field " .. tostring(k) .. " in message " .. tostring(c_type) .. " is unknown")
587 local lua2c = vpp.t_lua2c[field.c_type]
588 -- print("__MSG__ field " .. tostring(k) .. " : " .. vpp.dump(field))
589 -- if the field is not an array type, try to coerce the argument to a number
590 if not field.array and type(v) == "string" then
594 print("__MSG__ " .. tostring(c_type) .. " t_lua2c: can not store field " .. field.name ..
595 " type " .. field.c_type .. " dst " .. tostring(dst[k]))
600 if field.array and (type(v) == "table") then
601 -- print("NTFY: field " .. tostring(k) .. " in message " .. tostring(c_type) .. " is an array")
602 for field_i, field_v in ipairs(v) do
603 -- print("NTFY: setting member#" .. tostring(field_i) .. " to value " .. vpp.dump(field_v))
604 local field_len, field_val = lua2c(field.c_type, field_v, dst[k][field_i-1])
605 len = len + field_len
608 len, val = lua2c(field.c_type, v, dst[k])
610 if not field.array then
613 if 0 == field.array then
614 additional_len = additional_len + len
615 -- print("Adding " .. tostring(len) .. " bytes due to field " .. tostring(field.name))
616 -- If there is a variable storing the length
617 -- and the input table does not set it, do magic
618 if field.array_size and not src[field.array_size] then
619 local size_field = fields_info[field.array_size]
621 dst[field.array_size] = vpp.t_c2lua[size_field.c_type](size_field.c_type, len)
626 -- print("Full message:\n" .. vpp.hex_dump(ffi.string(ffi.cast('void *', req_store_cache), 64)))
628 return (ffi.sizeof(dst[0])+additional_len)
631 vpp.t_c2lua["__MSG__"] = function(c_type, src_ptr, src_len)
633 local reply_typed_ptr = ffi.cast(c_type .. " *", src_ptr)
634 local field_desc = vpp.c_type_to_fields[c_type]
636 for i = 0,src_len-1 do
637 out[i+1] = vpp.t_c2lua[c_type](c_type, src_ptr[i])
642 for k, v in pairs(field_desc) do
643 local v_c2lua = vpp.t_c2lua[v.c_type]
648 local len_field_name = k .. "_length"
649 local len_field = field_desc[len_field_name]
651 local real_len = vpp.t_c2lua[len_field.c_type](len_field.c_type, reply_typed_ptr[len_field_name])
652 out[k] = v_c2lua(v.c_type, reply_typed_ptr[k], real_len)
654 -- check if len = 0, then must be a field which contains the size
655 len_field = field_desc[v.array_size]
656 local real_len = vpp.t_c2lua[len_field.c_type](len_field.c_type, reply_typed_ptr[v.array_size])
657 -- print("REAL length: " .. vpp.dump(v) .. " : " .. tostring(real_len))
658 out[k] = v_c2lua(v.c_type, reply_typed_ptr[k], real_len)
660 -- alas, just stuff the entire array
661 out[k] = v_c2lua(v.c_type, reply_typed_ptr[k], len)
664 out[k] = v_c2lua(v.c_type, reply_typed_ptr[k])
667 out[k] = "<no accessor function for type " .. tostring(v.c_type) .. ">"
677 function vpp.resolve_message_number(msgname)
678 local name = msgname .. "_" .. vpp.msg_name_to_crc[msgname]
679 local idx = vpp.vac.vac_get_msg_index(vpp.c_str(name))
680 if vpp.debug_dump then
681 print("Index for " .. tostring(name) .. " is " .. tostring(idx))
683 vpp.msg_name_to_number[msgname] = idx
684 vpp.msg_number_to_name[idx] = msgname
685 vpp.msg_number_to_type[idx] = "vl_api_" .. msgname .. "_t"
686 vpp.msg_number_to_pointer_type[idx] = vpp.msg_number_to_type[idx] .. " *"
687 ffi.cdef("\n\n enum { vl_msg_" .. msgname .. " = " .. idx .. " };\n\n")
690 function vpp.connect(vpp, client_name)
691 local name = "lua_client"
695 local ret = vpp.vac.vac_connect(vpp.c_str(client_name), nil, nil)
696 if tonumber(ret) == 0 then
697 vpp.is_connected = true
699 for k, v in pairs(vpp.msg_name_to_number) do
700 vpp.resolve_message_number(k)
704 function vpp.disconnect(vpp)
705 vpp.vac.vac_disconnect()
708 function vpp.json_api(vpp, path, plugin_name)
709 -- print("Consuming the VPP api from "..path)
711 local f = io.open(path, "r")
713 print("Could not open " .. path)
716 local data = f:read("*all")
717 local json = json.parse(data)
718 if not (json.types or json.messages) then
719 print("Can not parse " .. path)
725 for i, v in ipairs(json.types) do
726 table.insert(all_types, { typeonly = 1, desc = v })
728 for i, v in ipairs(json.messages) do
729 table.insert(all_types, { typeonly = 0, desc = v })
731 for i, v in ipairs(all_types) do
732 local typeonly = v.typeonly
733 local name = v.desc[1]
734 local c_type = "vl_api_" .. name .. "_t"
737 -- vpp.msg_name_to_fields[name] = fields
738 -- print("CTYPE " .. c_type)
739 vpp.c_type_to_fields[c_type] = fields
740 vpp.t_lua2c[c_type] = vpp.t_lua2c["__MSG__"]
741 vpp.t_c2lua[c_type] = vpp.t_c2lua["__MSG__"]
743 local cdef = { "\n\n#pragma pack(1)\ntypedef struct _vl_api_", name, " {\n" }
744 for ii, vv in ipairs(v.desc) do
745 if type(vv) == "table" then
747 vpp.msg_name_to_crc[name] = string.sub(vv.crc, 3) -- strip the leading 0x
749 local fieldtype = vv[1]
750 local fieldname = vv[2]
751 local fieldcount = vv[3]
752 local fieldcountvar = vv[4]
753 local fieldrec = { name = fieldname, c_type = fieldtype, array = fieldcount, array_size = fieldcountvar }
755 table.insert(cdef, " " .. fieldtype .. " " .. fieldname .. "[" .. fieldcount .. "];\n")
756 if fieldtype == "u8" then
757 -- any array of bytes is treated as a string
758 elseif vpp.t_lua2c[fieldtype] then
759 -- print("Array of " .. fieldtype .. " is ok!")
761 print("Unknown array type: ", name, " : " , fieldname, " : ", fieldtype, ":", fieldcount, ":", fieldcountvar)
764 table.insert(cdef, " " .. fieldtype .. " " .. fieldname .. ";\n")
766 fields[fieldname] = fieldrec
771 table.insert(cdef, "} vl_api_" .. name .. "_t;")
772 table.insert(ffii, table.concat(cdef))
774 if typeonly == 0 then
775 -- we will want to resolve this later
776 if vpp.debug_dump then
777 print("Remember to resolve " .. name)
779 vpp.msg_name_to_number[name] = -1
780 if vpp.is_connected then
781 vpp.resolve_message_number(name)
786 local cdef_full = table.concat(ffii)
790 function vpp.consume_api(vpp, path, plugin_name)
791 -- print("Consuming the VPP api from "..path)
793 local f = io.open(path, "r")
795 print("Could not open " .. path)
798 local data = f:read("*all")
799 -- Remove all C comments
800 data = data:gsub("/%*.-%*/", "")
801 if vpp.is_connected and not plugin_name then
802 print(path .. ": must specify plugin name!")
806 vpp.plugin_version[plugin_name] = vpp.crc_version_string(data)
807 local full_plugin_name = plugin_name .. "_" .. vpp.plugin_version[plugin_name]
808 local reply = vpp:api_call("get_first_msg_id", { name = full_plugin_name } )
809 vpp.next_msg_num = tonumber(reply[1].first_msg_id)
810 print("Plugin " .. full_plugin_name .. " first message is " .. tostring(vpp.next_msg_num))
812 -- print ("data len: ", #data)
813 data = data:gsub("\n(.-)(%S+)%s*{([^}]*)}", function (preamble, name, members)
814 local _, typeonly = preamble:gsub("typeonly", "")
815 local maybe_msg_id_field = { [0] = "u16 _vl_msg_id;", "" }
816 local onedef = "\n\n#pragma pack(1)\ntypedef struct _vl_api_"..name.. " {\n" ..
817 -- " u16 _vl_msg_id;" ..
818 maybe_msg_id_field[typeonly] ..
819 members:gsub("%[[a-zA-Z_]+]", "[0]") ..
820 "} vl_api_" .. name .. "_t;"
822 local c_type = "vl_api_" .. name .. "_t"
825 -- vpp.msg_name_to_fields[name] = fields
826 -- print("CTYPE " .. c_type)
827 vpp.c_type_to_fields[c_type] = fields
828 vpp.t_lua2c[c_type] = vpp.t_lua2c["__MSG__"]
829 vpp.t_c2lua[c_type] = vpp.t_c2lua["__MSG__"]
830 local mirec = { name = "_vl_msg_id", c_type = "u16", array = nil, array_size = nil }
831 if typeonly == 0 then
832 fields[mirec.name] = mirec
835 -- populate the field reflection table for the message
836 -- sets the various type information as well as the accessors for lua<->C conversion
837 members:gsub("(%S+)%s+(%S+);", function (fieldtype, fieldname)
838 local fieldcount = nil
839 local fieldcountvar = nil
840 -- data = data:gsub("%[[a-zA-Z_]+]", "[0]")
841 fieldname = fieldname:gsub("(%b[])", function(cnt)
842 fieldcount = tonumber(cnt:sub(2, -2));
843 if not fieldcount then
845 fieldcountvar = cnt:sub(2, -2)
849 local fieldrec = { name = fieldname, c_type = fieldtype, array = fieldcount, array_size = fieldcountvar }
851 if fieldtype == "u8" then
852 -- any array of bytes is treated as a string
853 elseif vpp.t_lua2c[fieldtype] then
854 -- print("Array of " .. fieldtype .. " is ok!")
856 print("Unknown array type: ", name, " : " , fieldname, " : ", fieldtype, ":", fieldcount, ":", fieldcountvar)
859 fields[fieldname] = fieldrec
862 -- print(dump(fields))
864 if typeonly == 0 then
865 local this_message_number = vpp.next_msg_num
866 vpp.next_msg_num = vpp.next_msg_num + 1
867 vpp.msg_name_to_number[name] = this_message_number
868 vpp.msg_number_to_name[this_message_number] = name
869 vpp.msg_number_to_type[this_message_number] = "vl_api_" .. name .. "_t"
870 vpp.msg_number_to_pointer_type[this_message_number] = vpp.msg_number_to_type[this_message_number] .. " *"
871 onedef = onedef .. "\n\n enum { vl_msg_" .. name .. " = " .. this_message_number .. " };\n\n"
873 table.insert(ffii, onedef);
876 local cdef = table.concat(ffii)
882 function vpp.lua2c(vpp, c_type, src, dst_c_ptr)
883 -- returns the number of bytes written to memory pointed by dst
884 local lua2c = vpp.t_lua2c[c_type]
886 return(lua2c(c_type, src, dst_c_ptr))
888 print("vpp.lua2c: do not know how to store type " .. tostring(c_type))
894 function vpp.c2lua(vpp, c_type, src_ptr, src_len)
895 -- returns the lua data structure
896 local c2lua = vpp.t_c2lua[c_type]
898 return(c2lua(c_type, src_ptr, src_len))
900 print("vpp.c2lua: do not know how to load type " .. c_type)
905 local req_store_cache = ffi.new("vl_api_opaque_message_t[1]")
907 function vpp.api_write(vpp, api_name, req_table)
908 local msg_num = vpp.msg_name_to_number[api_name]
910 print ("API call "..api_name.." is not known")
914 if not req_table then
917 req_table._vl_msg_id = msg_num
919 local packed_len = vpp:lua2c(vpp.msg_number_to_type[msg_num], req_table, req_store_cache)
920 if vpp.debug_dump then
921 print("Write Message length: " .. tostring(packed_len) .. "\n" .. vpp.hex_dump(ffi.string(ffi.cast('void *', req_store_cache), packed_len)))
924 res = vpp.vac.vac_write(ffi.cast('void *', req_store_cache), packed_len)
928 local rep_store_cache = ffi.new("vl_api_opaque_message_t *[1]")
929 local rep_len_cache = ffi.new("int[1]")
931 function vpp.api_read(vpp)
932 local rep_type = "vl_api_opaque_message_t"
933 local rep = rep_store_cache
934 local replen = rep_len_cache
935 res = vpp.vac.vac_read(ffi.cast("void *", rep), replen)
936 if vpp.debug_dump then
937 print("Read Message length: " .. tostring(replen[0]) .. "\n" .. vpp.hex_dump(ffi.string(ffi.cast('void *', rep[0]), replen[0])))
940 local reply_msg_num = ffi.C.ntohs(rep[0]._vl_msg_id)
941 local reply_msg_name = vpp.msg_number_to_name[reply_msg_num]
943 local reply_typed_ptr = ffi.cast(vpp.msg_number_to_pointer_type[reply_msg_num], rep[0])
944 local out = vpp:c2lua(vpp.msg_number_to_type[reply_msg_num], rep[0], nil, replen[0])
945 if type(out) == "table" then
946 out["luaapi_message_name"] = reply_msg_name
949 vpp.vac.vac_free(ffi.cast('void *',rep[0]))
951 return reply_msg_name, out
954 function vpp.api_call(vpp, api_name, req_table, options_in)
955 local msg_num = vpp.msg_name_to_number[api_name]
956 local end_message_name = api_name .."_reply"
959 local options = options_in or {}
961 if vpp.debug_dump then
962 print("Message #" .. tostring(msg_num) .. " for name " .. tostring(api_name))
964 vpp:api_write(api_name, req_table)
965 if not vpp.msg_name_to_number[end_message_name] or options.force_ping then
966 end_message_name = "control_ping_reply"
967 vpp:api_write("control_ping")
970 reply_message_name, reply = vpp:api_read()
971 if reply and not reply.context then
972 -- there may be async events inbetween
973 table.insert(vpp.events, reply)
975 if reply_message_name ~= "control_ping_reply" then
976 -- do not insert the control ping encapsulation
977 table.insert(replies, reply)
981 until reply_message_name == end_message_name
983 print(api_name .. " is an unknown API call")