blob: c88b456d260dc7e58b02bef5e3a6801704ff9c10 [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);
Andrew Yourtchenko985f3d12016-11-25 13:29:30 +0000276 int pneum_connect(char *name, char *chroot_prefix, void *cb);
Andrew Yourtchenkofa1456a2016-11-11 16:32:52 +0000277 int pneum_disconnect(void);
278 int pneum_read(char **data, int *l);
279 int pneum_write(char *data, int len);
Andrew Yourtchenko985f3d12016-11-25 13:29:30 +0000280 void pneum_free(char *data);
Andrew Yourtchenkofa1456a2016-11-11 16:32:52 +0000281]]
282
283 vpp.pneum_path = args.pneum_path
284 ffi.cdef(pneum_api)
285 local init_res = 0
Andrew Yourtchenko985f3d12016-11-25 13:29:30 +0000286 vpp.pneum = ffi.load(vpp.pneum_path)
Andrew Yourtchenkofa1456a2016-11-11 16:32:52 +0000287 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
Andrew Yourtchenko985f3d12016-11-25 13:29:30 +0000450 -- print("NTFY: field " .. tostring(k) .. " in message " .. tostring(c_type) .. " is an array")
Andrew Yourtchenkofa1456a2016-11-11 16:32:52 +0000451 for field_i, field_v in ipairs(v) do
Andrew Yourtchenko985f3d12016-11-25 13:29:30 +0000452 -- print("NTFY: setting member#" .. tostring(field_i) .. " to value " .. vpp.dump(field_v))
Andrew Yourtchenkofa1456a2016-11-11 16:32:52 +0000453 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
524end
525
526function vpp.connect(vpp, client_name)
527 local name = "lua_client"
528 if client_name then
529 name = client_name
530 end
Andrew Yourtchenko985f3d12016-11-25 13:29:30 +0000531 local ret = vpp.pneum.pneum_connect(vpp.c_str(client_name), nil, nil)
Andrew Yourtchenkofa1456a2016-11-11 16:32:52 +0000532 if tonumber(ret) == 0 then
533 vpp.is_connected = true
534 end
535 end
536
537function vpp.disconnect(vpp)
538 vpp.pneum.pneum_disconnect()
539 end
540
541function 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
633function 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
642end
643
644function 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
653end
654
655local req_store_cache = ffi.new("vl_api_opaque_message_t[1]")
656
657function 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
678local rep_store_cache = ffi.new("vl_api_opaque_message_t *[1]")
679local rep_len_cache = ffi.new("int[1]")
680
681function 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
Andrew Yourtchenko985f3d12016-11-25 13:29:30 +0000699 vpp.pneum.pneum_free(ffi.cast('void *',rep[0]))
Andrew Yourtchenkofa1456a2016-11-11 16:32:52 +0000700
701 return reply_msg_name, out
702 end
703
704function 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
736return vpp