blob: 587eb110da485f544eb4515d14b2da793471d873 [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
Andrew Yourtchenkob868e4e2016-12-08 14:03:55 +000018-- json decode/encode from https://gist.github.com/tylerneylon/59f4bcf316be525b30ab
19-- licensed by the author tylerneylon into public domain. Thanks!
20
21local json = {}
22
23-- Internal functions.
24
25local function kind_of(obj)
26 if type(obj) ~= 'table' then return type(obj) end
27 local i = 1
28 for _ in pairs(obj) do
29 if obj[i] ~= nil then i = i + 1 else return 'table' end
30 end
31 if i == 1 then return 'table' else return 'array' end
32end
33
34local 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])
39 end
40 return s
41end
42
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.
47local 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)
52 end
53 return pos, false
54 end
55 return pos + 1, true
56end
57
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.
60local function parse_str_val(str, pos, val)
61 val = val or ''
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))
72end
73
74-- Returns val, pos; the returned pos is after the number's final character.
75local 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
80end
81
82
83-- Public values and functions.
84
85function 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
90 s[#s + 1] = '['
91 for i, val in ipairs(obj) do
92 if i > 1 then s[#s + 1] = ', ' end
93 s[#s + 1] = json.stringify(val)
94 end
95 s[#s + 1] = ']'
96 elseif kind == 'table' then
97 if as_key then error('Can\'t encode table as key.') end
98 s[#s + 1] = '{'
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)
102 s[#s + 1] = ':'
103 s[#s + 1] = json.stringify(v)
104 end
105 s[#s + 1] = '}'
106 elseif kind == 'string' then
107 return '"' .. escape_str(obj) .. '"'
108 elseif kind == 'number' then
109 if as_key then return '"' .. tostring(obj) .. '"' end
110 return tostring(obj)
111 elseif kind == 'boolean' then
112 return tostring(obj)
113 elseif kind == 'nil' then
114 return 'null'
115 else
116 error('Unjsonifiable type: ' .. kind .. '.')
117 end
118 return table.concat(s)
119end
120
121json.null = {} -- This is a one-off table to represent the null value.
122
123function json.parse(str, pos, end_delim)
124 pos = pos or 1
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
130 pos = pos + 1
131 while true do
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, ',')
138 end
139 elseif first == '[' then -- Parse an array.
140 local arr, val, delim_found = {}, true, true
141 pos = pos + 1
142 while true do
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
146 arr[#arr + 1] = val
147 pos, delim_found = skip_delim(str, pos, ',')
148 end
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.
154 return nil, pos + 1
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
160 end
161 local pos_info_str = 'position ' .. pos .. ': ' .. str:sub(pos, pos + 10)
162 error('Invalid json syntax starting at ' .. pos_info_str)
163 end
164end
165
166
Andrew Yourtchenkofa1456a2016-11-11 16:32:52 +0000167local vpp = {}
168
169local ffi = require("ffi")
170
171--[[
172
173The basic type definitions. A bit of weird gymnastic with
174unionization of the hton* and ntoh* functions results
175is to make handling of signed and unsigned types a bit cleaner,
176essentially building typecasting into a C union.
177
178The vl_api_opaque_message_t is a synthetic type assumed to have
179enough storage to hold the entire API message regardless of the type.
180During the operation it is casted to the specific message struct types.
181
182]]
183
184
185ffi.cdef([[
186
187typedef uint8_t u8;
188typedef int8_t i8;
189typedef uint16_t u16;
190typedef int16_t i16;
191typedef uint32_t u32;
192typedef int32_t i32;
193typedef uint64_t u64;
194typedef int64_t i64;
195typedef double f64;
196typedef float f32;
197
198#pragma pack(1)
199typedef union {
200 u16 u16;
201 i16 i16;
202} lua_ui16t;
203
204#pragma pack(1)
205typedef union {
206 u32 u32;
207 i32 i32;
208} lua_ui32t;
209
210u16 ntohs(uint16_t hostshort);
211u16 htons(uint16_t hostshort);
212u32 htonl(uint32_t along);
213u32 ntohl(uint32_t along);
214void *memset(void *s, int c, size_t n);
215void *memcpy(void *dest, void *src, size_t n);
216
217#pragma pack(1)
218typedef struct _vl_api_opaque_message {
219 u16 _vl_msg_id;
220 u8 data[65536];
221} vl_api_opaque_message_t;
222]])
223
224
225-- CRC-based version stuff
226
227local 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 }
292);
293
294local function CRC8(crc, d)
295 return bit.bxor(bit.rshift(crc, 8), crc32c_table[bit.band(0xff, bit.bxor(crc, d))])
296end
297
298local function CRC16(crc, d)
299 crc = CRC8(crc, bit.band(d, 0xFF))
300 d = bit.rshift(d, 8)
301 crc = CRC8(crc, bit.band(d, 0xFF))
302 return crc
303end
304
305local function string_crc(str, crc)
306 for i=1,#str do
307 -- print("S", i, string.byte(str, i), string.char(string.byte(str, i)))
308 crc = CRC8(crc, string.byte(str, i))
309 end
310 return crc
311end
312
313local tokens = {
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 } },
348}
349
350
351function vpp.crc_version_string(data)
352 local input_crc = 0
353 -- Get rid of comments
354 data = data:gsub("/%*.-%*/", "")
355 data = data:gsub("//[^\n]+", "")
356 -- print(data)
357 idx = 1
358 while (true) do
359 local matched = nil
360 for k, v in ipairs(tokens) do
361 if not matched then
362 local x, y, cap = string.find(data, v["match"], idx)
363 if x == idx then
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] )
366 end
367 end
368 end
369 if matched then
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)
377 end
378 -- print(vpp.dump(matched))
379 end
380 else
381 -- print("NOT MATCHED!")
382 local crc = CRC16(input_crc, 0xFFFFFFFF)
383 return string.sub(string.format("%x", crc), -8)
384 end
385 end
386end
387
388
389function vpp.dump(o)
390 if type(o) == 'table' then
391 local s = '{ '
392 for k,v in pairs(o) do
393 if type(k) ~= 'number' then k = '"'..k..'"' end
394 s = s .. '['..k..'] = ' .. vpp.dump(v) .. ','
395 end
396 return s .. '} '
397 else
398 return tostring(o)
399 end
400end
401
402function vpp.hex_dump(buf)
403 local ret = {}
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
409 end
410 return table.concat(ret)
411end
412
413
414function 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)
418 return c_str
419end
420
421
422function vpp.init(vpp, args)
Damjan Marion5fec1e82017-04-13 19:13:47 +0200423 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);
Andrew Yourtchenkofa1456a2016-11-11 16:32:52 +0000431]]
432
Damjan Marion5fec1e82017-04-13 19:13:47 +0200433 vpp.vac_path = args.vac_path
434 ffi.cdef(vac_api)
Andrew Yourtchenkofa1456a2016-11-11 16:32:52 +0000435 local init_res = 0
Damjan Marion5fec1e82017-04-13 19:13:47 +0200436 vpp.vac = ffi.load(vpp.vac_path)
Andrew Yourtchenkofa1456a2016-11-11 16:32:52 +0000437 if (init_res < 0) then
438 return nil
439 end
440
441 vpp.next_msg_num = 1
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 = {}
Andrew Yourtchenkob868e4e2016-12-08 14:03:55 +0000447 vpp.msg_name_to_crc = {}
Andrew Yourtchenkofa1456a2016-11-11 16:32:52 +0000448 vpp.c_type_to_fields = {}
449 vpp.events = {}
450 vpp.plugin_version = {}
451 vpp.is_connected = false
452
453
454 vpp.t_lua2c = {}
455 vpp.t_c2lua = {}
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)
461 return(#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
465 end
466 return(#src)
467 else
468 return 1, src -- ffi.cast("u8", src)
469 end
470 end
471 vpp.t_c2lua["u8"] = function(c_type, src_ptr, src_len)
472 if src_len then
473 return ffi.string(src_ptr, src_len)
474 else
475 return (tonumber(src_ptr))
476 end
477 end
478
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)
483 end
484 return(2 * #src)
485 else
486 return 2, (ffi.C.htons(src))
487 end
488 end
489 vpp.t_c2lua["u16"] = function(c_type, src_ptr, src_len)
490 if src_len then
491 local out = {}
492 for i = 0,src_len-1 do
493 out[i+1] = tonumber(ffi.C.ntohs(src_ptr[i]))
494 end
495 return out
496 else
497 return (tonumber(ffi.C.ntohs(src_ptr)))
498 end
499 end
500
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)
505 end
506 return(4 * #src)
507 else
508 return 4, (ffi.C.htonl(src))
509 end
510 end
511 vpp.t_c2lua["u32"] = function(c_type, src_ptr, src_len)
512 if src_len then
513 local out = {}
514 for i = 0,src_len-1 do
515 out[i+1] = tonumber(ffi.C.ntohl(src_ptr[i]))
516 end
517 return out
518 else
519 return (tonumber(ffi.C.ntohl(src_ptr)))
520 end
521 end
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)
526 end
527 return(4 * #src)
528 else
529 return 4, (ffi.C.htonl(src))
530 end
531 end
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))
537 return out
538 end
539 if src_len then
540 local out = {}
541 for i = 0,src_len-1 do
542 out[i+1] = tonumber(ntohl(src_ptr[i]))
543 end
544 else
545 return (tonumber(ntohl(src_ptr)))
546 end
547 end
548
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
553 end
554 return(8 * #src)
555 else
556 return 8, ffi.cast("u64", src) --- FIXME ENDIAN
557 end
558 end
559 vpp.t_c2lua["u64"] = function(c_type, src_ptr, src_len)
560 if src_len then
561 local out = {}
562 for i = 0,src_len-1 do
563 out[i+1] = tonumber(src_ptr[i]) -- FIXME ENDIAN
564 end
565 return out
566 else
567 return (tonumber(src_ptr)) --FIXME ENDIAN
568 end
569 end
570
571
572
573
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]
584 if not field then
585 print("ERROR: field " .. tostring(k) .. " in message " .. tostring(c_type) .. " is unknown")
586 end
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
591 v = tonumber(v)
592 end
593 if not lua2c then
594 print("__MSG__ " .. tostring(c_type) .. " t_lua2c: can not store field " .. field.name ..
595 " type " .. field.c_type .. " dst " .. tostring(dst[k]))
596 return 0
597 end
598 local len = 0
599 local val = nil
600 if field.array and (type(v) == "table") then
Andrew Yourtchenko985f3d12016-11-25 13:29:30 +0000601 -- print("NTFY: field " .. tostring(k) .. " in message " .. tostring(c_type) .. " is an array")
Andrew Yourtchenkofa1456a2016-11-11 16:32:52 +0000602 for field_i, field_v in ipairs(v) do
Andrew Yourtchenko985f3d12016-11-25 13:29:30 +0000603 -- print("NTFY: setting member#" .. tostring(field_i) .. " to value " .. vpp.dump(field_v))
Andrew Yourtchenkofa1456a2016-11-11 16:32:52 +0000604 local field_len, field_val = lua2c(field.c_type, field_v, dst[k][field_i-1])
605 len = len + field_len
606 end
607 else
608 len, val = lua2c(field.c_type, v, dst[k])
609 end
610 if not field.array then
611 dst[k] = val
612 else
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]
620 if size_field then
621 dst[field.array_size] = vpp.t_c2lua[size_field.c_type](size_field.c_type, len)
622 end
623 end
624 end
625 end
626 -- print("Full message:\n" .. vpp.hex_dump(ffi.string(ffi.cast('void *', req_store_cache), 64)))
627 end
628 return (ffi.sizeof(dst[0])+additional_len)
629 end
630
631 vpp.t_c2lua["__MSG__"] = function(c_type, src_ptr, src_len)
632 local out = {}
633 local reply_typed_ptr = ffi.cast(c_type .. " *", src_ptr)
634 local field_desc = vpp.c_type_to_fields[c_type]
635 if src_len then
636 for i = 0,src_len-1 do
637 out[i+1] = vpp.t_c2lua[c_type](c_type, src_ptr[i])
638 end
639 return out
640 end
641
642 for k, v in pairs(field_desc) do
643 local v_c2lua = vpp.t_c2lua[v.c_type]
644 if v_c2lua then
645 local len = v.array
646 -- print(dump(v))
647 if len then
648 local len_field_name = k .. "_length"
649 local len_field = field_desc[len_field_name]
650 if (len_field) then
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)
653 elseif len == 0 then
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)
659 else
660 -- alas, just stuff the entire array
661 out[k] = v_c2lua(v.c_type, reply_typed_ptr[k], len)
662 end
663 else
664 out[k] = v_c2lua(v.c_type, reply_typed_ptr[k])
665 end
666 else
667 out[k] = "<no accessor function for type " .. tostring(v.c_type) .. ">"
668 end
669 -- print(k, out[k])
670 end
671 return out
672 end
673
674 return vpp
675end
676
Andrew Yourtchenkob868e4e2016-12-08 14:03:55 +0000677function vpp.resolve_message_number(msgname)
678 local name = msgname .. "_" .. vpp.msg_name_to_crc[msgname]
Damjan Marion5fec1e82017-04-13 19:13:47 +0200679 local idx = vpp.vac.vac_get_msg_index(vpp.c_str(name))
Andrew Yourtchenkob868e4e2016-12-08 14:03:55 +0000680 if vpp.debug_dump then
681 print("Index for " .. tostring(name) .. " is " .. tostring(idx))
682 end
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")
688end
689
Andrew Yourtchenkofa1456a2016-11-11 16:32:52 +0000690function vpp.connect(vpp, client_name)
691 local name = "lua_client"
692 if client_name then
693 name = client_name
694 end
Damjan Marion5fec1e82017-04-13 19:13:47 +0200695 local ret = vpp.vac.vac_connect(vpp.c_str(client_name), nil, nil)
Andrew Yourtchenkofa1456a2016-11-11 16:32:52 +0000696 if tonumber(ret) == 0 then
697 vpp.is_connected = true
698 end
Andrew Yourtchenkob868e4e2016-12-08 14:03:55 +0000699 for k, v in pairs(vpp.msg_name_to_number) do
700 vpp.resolve_message_number(k)
701 end
Andrew Yourtchenkofa1456a2016-11-11 16:32:52 +0000702 end
703
704function vpp.disconnect(vpp)
Damjan Marion5fec1e82017-04-13 19:13:47 +0200705 vpp.vac.vac_disconnect()
Andrew Yourtchenkofa1456a2016-11-11 16:32:52 +0000706 end
707
Andrew Yourtchenkob868e4e2016-12-08 14:03:55 +0000708function vpp.json_api(vpp, path, plugin_name)
709 -- print("Consuming the VPP api from "..path)
710 local ffii = {}
711 local f = io.open(path, "r")
712 if not f then
713 print("Could not open " .. path)
714 return nil
715 end
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)
720 return nil
721 end
722
723 local all_types = {}
724
725 for i, v in ipairs(json.types) do
726 table.insert(all_types, { typeonly = 1, desc = v })
727 end
728 for i, v in ipairs(json.messages) do
729 table.insert(all_types, { typeonly = 0, desc = v })
730 end
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"
735
736 local fields = {}
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__"]
742
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
746 if vv.crc then
747 vpp.msg_name_to_crc[name] = string.sub(vv.crc, 3) -- strip the leading 0x
748 else
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 }
754 if fieldcount then
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!")
760 else
761 print("Unknown array type: ", name, " : " , fieldname, " : ", fieldtype, ":", fieldcount, ":", fieldcountvar)
762 end
763 else
764 table.insert(cdef, " " .. fieldtype .. " " .. fieldname .. ";\n")
765 end
766 fields[fieldname] = fieldrec
767 end
768 end
769 end
770
771 table.insert(cdef, "} vl_api_" .. name .. "_t;")
772 table.insert(ffii, table.concat(cdef))
773
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)
778 end
779 vpp.msg_name_to_number[name] = -1
780 if vpp.is_connected then
781 vpp.resolve_message_number(name)
782 end
783 end
784
785 end
786 local cdef_full = table.concat(ffii)
787 ffi.cdef(cdef_full)
788end
789
Andrew Yourtchenkofa1456a2016-11-11 16:32:52 +0000790function vpp.consume_api(vpp, path, plugin_name)
791 -- print("Consuming the VPP api from "..path)
792 local ffii = {}
793 local f = io.open(path, "r")
794 if not f then
795 print("Could not open " .. path)
796 return nil
797 end
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!")
803 return
804 end
805 if plugin_name then
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))
811 end
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;"
821
822 local c_type = "vl_api_" .. name .. "_t"
823
824 local fields = {}
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
833 end
834
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
844 fieldcount = 0
845 fieldcountvar = cnt:sub(2, -2)
846 end
847 return ""
848 end)
849 local fieldrec = { name = fieldname, c_type = fieldtype, array = fieldcount, array_size = fieldcountvar }
850 if fieldcount then
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!")
855 else
856 print("Unknown array type: ", name, " : " , fieldname, " : ", fieldtype, ":", fieldcount, ":", fieldcountvar)
857 end
858 end
859 fields[fieldname] = fieldrec
860 end)
861
862 -- print(dump(fields))
863
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"
872 end
873 table.insert(ffii, onedef);
874 return "";
875 end)
876 local cdef = table.concat(ffii)
877 -- print(cdef)
878 ffi.cdef(cdef)
879 end
880
881
882function 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]
885 if lua2c then
886 return(lua2c(c_type, src, dst_c_ptr))
887 else
Andrew Yourtchenkob868e4e2016-12-08 14:03:55 +0000888 print("vpp.lua2c: do not know how to store type " .. tostring(c_type))
889 local x = "a" .. nil
Andrew Yourtchenkofa1456a2016-11-11 16:32:52 +0000890 return 0
891 end
892end
893
894function vpp.c2lua(vpp, c_type, src_ptr, src_len)
895 -- returns the lua data structure
896 local c2lua = vpp.t_c2lua[c_type]
897 if c2lua then
898 return(c2lua(c_type, src_ptr, src_len))
899 else
900 print("vpp.c2lua: do not know how to load type " .. c_type)
901 return nil
902 end
903end
904
905local req_store_cache = ffi.new("vl_api_opaque_message_t[1]")
906
907function vpp.api_write(vpp, api_name, req_table)
908 local msg_num = vpp.msg_name_to_number[api_name]
909 if not msg_num then
910 print ("API call "..api_name.." is not known")
911 return nil
912 end
913
914 if not req_table then
915 req_table = {}
916 end
917 req_table._vl_msg_id = msg_num
918
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)))
922 end
923
Damjan Marion5fec1e82017-04-13 19:13:47 +0200924 res = vpp.vac.vac_write(ffi.cast('void *', req_store_cache), packed_len)
Andrew Yourtchenkofa1456a2016-11-11 16:32:52 +0000925 return res
926 end
927
928local rep_store_cache = ffi.new("vl_api_opaque_message_t *[1]")
929local rep_len_cache = ffi.new("int[1]")
930
931function 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
Damjan Marion5fec1e82017-04-13 19:13:47 +0200935 res = vpp.vac.vac_read(ffi.cast("void *", rep), replen)
Andrew Yourtchenkofa1456a2016-11-11 16:32:52 +0000936 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])))
938 end
939
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]
942
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
947 end
948
Damjan Marion5fec1e82017-04-13 19:13:47 +0200949 vpp.vac.vac_free(ffi.cast('void *',rep[0]))
Andrew Yourtchenkofa1456a2016-11-11 16:32:52 +0000950
951 return reply_msg_name, out
952 end
953
954function 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"
957 local replies = {}
958 local cstruct = ""
959 local options = options_in or {}
960 if msg_num then
Andrew Yourtchenkob868e4e2016-12-08 14:03:55 +0000961 if vpp.debug_dump then
962 print("Message #" .. tostring(msg_num) .. " for name " .. tostring(api_name))
963 end
Andrew Yourtchenkofa1456a2016-11-11 16:32:52 +0000964 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")
968 end
969 repeat
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)
974 else
975 if reply_message_name ~= "control_ping_reply" then
976 -- do not insert the control ping encapsulation
977 table.insert(replies, reply)
978 end
979 end
980 -- print(reply)
981 until reply_message_name == end_message_name
982 else
983 print(api_name .. " is an unknown API call")
984 return nil
985 end
986 return replies
987 end
988
989return vpp