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.
20 local ffi = require("ffi")
24 The basic type definitions. A bit of weird gymnastic with
25 unionization of the hton* and ntoh* functions results
26 is to make handling of signed and unsigned types a bit cleaner,
27 essentially building typecasting into a C union.
29 The vl_api_opaque_message_t is a synthetic type assumed to have
30 enough storage to hold the entire API message regardless of the type.
31 During the operation it is casted to the specific message struct types.
61 u16 ntohs(uint16_t hostshort);
62 u16 htons(uint16_t hostshort);
63 u32 htonl(uint32_t along);
64 u32 ntohl(uint32_t along);
65 void *memset(void *s, int c, size_t n);
66 void *memcpy(void *dest, void *src, size_t n);
69 typedef struct _vl_api_opaque_message {
72 } vl_api_opaque_message_t;
76 -- CRC-based version stuff
78 local crc32c_table = ffi.new('const uint32_t[256]',
79 { 0x00000000, 0xF26B8303, 0xE13B70F7, 0x1350F3F4,
80 0xC79A971F, 0x35F1141C, 0x26A1E7E8, 0xD4CA64EB,
81 0x8AD958CF, 0x78B2DBCC, 0x6BE22838, 0x9989AB3B,
82 0x4D43CFD0, 0xBF284CD3, 0xAC78BF27, 0x5E133C24,
83 0x105EC76F, 0xE235446C, 0xF165B798, 0x030E349B,
84 0xD7C45070, 0x25AFD373, 0x36FF2087, 0xC494A384,
85 0x9A879FA0, 0x68EC1CA3, 0x7BBCEF57, 0x89D76C54,
86 0x5D1D08BF, 0xAF768BBC, 0xBC267848, 0x4E4DFB4B,
87 0x20BD8EDE, 0xD2D60DDD, 0xC186FE29, 0x33ED7D2A,
88 0xE72719C1, 0x154C9AC2, 0x061C6936, 0xF477EA35,
89 0xAA64D611, 0x580F5512, 0x4B5FA6E6, 0xB93425E5,
90 0x6DFE410E, 0x9F95C20D, 0x8CC531F9, 0x7EAEB2FA,
91 0x30E349B1, 0xC288CAB2, 0xD1D83946, 0x23B3BA45,
92 0xF779DEAE, 0x05125DAD, 0x1642AE59, 0xE4292D5A,
93 0xBA3A117E, 0x4851927D, 0x5B016189, 0xA96AE28A,
94 0x7DA08661, 0x8FCB0562, 0x9C9BF696, 0x6EF07595,
95 0x417B1DBC, 0xB3109EBF, 0xA0406D4B, 0x522BEE48,
96 0x86E18AA3, 0x748A09A0, 0x67DAFA54, 0x95B17957,
97 0xCBA24573, 0x39C9C670, 0x2A993584, 0xD8F2B687,
98 0x0C38D26C, 0xFE53516F, 0xED03A29B, 0x1F682198,
99 0x5125DAD3, 0xA34E59D0, 0xB01EAA24, 0x42752927,
100 0x96BF4DCC, 0x64D4CECF, 0x77843D3B, 0x85EFBE38,
101 0xDBFC821C, 0x2997011F, 0x3AC7F2EB, 0xC8AC71E8,
102 0x1C661503, 0xEE0D9600, 0xFD5D65F4, 0x0F36E6F7,
103 0x61C69362, 0x93AD1061, 0x80FDE395, 0x72966096,
104 0xA65C047D, 0x5437877E, 0x4767748A, 0xB50CF789,
105 0xEB1FCBAD, 0x197448AE, 0x0A24BB5A, 0xF84F3859,
106 0x2C855CB2, 0xDEEEDFB1, 0xCDBE2C45, 0x3FD5AF46,
107 0x7198540D, 0x83F3D70E, 0x90A324FA, 0x62C8A7F9,
108 0xB602C312, 0x44694011, 0x5739B3E5, 0xA55230E6,
109 0xFB410CC2, 0x092A8FC1, 0x1A7A7C35, 0xE811FF36,
110 0x3CDB9BDD, 0xCEB018DE, 0xDDE0EB2A, 0x2F8B6829,
111 0x82F63B78, 0x709DB87B, 0x63CD4B8F, 0x91A6C88C,
112 0x456CAC67, 0xB7072F64, 0xA457DC90, 0x563C5F93,
113 0x082F63B7, 0xFA44E0B4, 0xE9141340, 0x1B7F9043,
114 0xCFB5F4A8, 0x3DDE77AB, 0x2E8E845F, 0xDCE5075C,
115 0x92A8FC17, 0x60C37F14, 0x73938CE0, 0x81F80FE3,
116 0x55326B08, 0xA759E80B, 0xB4091BFF, 0x466298FC,
117 0x1871A4D8, 0xEA1A27DB, 0xF94AD42F, 0x0B21572C,
118 0xDFEB33C7, 0x2D80B0C4, 0x3ED04330, 0xCCBBC033,
119 0xA24BB5A6, 0x502036A5, 0x4370C551, 0xB11B4652,
120 0x65D122B9, 0x97BAA1BA, 0x84EA524E, 0x7681D14D,
121 0x2892ED69, 0xDAF96E6A, 0xC9A99D9E, 0x3BC21E9D,
122 0xEF087A76, 0x1D63F975, 0x0E330A81, 0xFC588982,
123 0xB21572C9, 0x407EF1CA, 0x532E023E, 0xA145813D,
124 0x758FE5D6, 0x87E466D5, 0x94B49521, 0x66DF1622,
125 0x38CC2A06, 0xCAA7A905, 0xD9F75AF1, 0x2B9CD9F2,
126 0xFF56BD19, 0x0D3D3E1A, 0x1E6DCDEE, 0xEC064EED,
127 0xC38D26C4, 0x31E6A5C7, 0x22B65633, 0xD0DDD530,
128 0x0417B1DB, 0xF67C32D8, 0xE52CC12C, 0x1747422F,
129 0x49547E0B, 0xBB3FFD08, 0xA86F0EFC, 0x5A048DFF,
130 0x8ECEE914, 0x7CA56A17, 0x6FF599E3, 0x9D9E1AE0,
131 0xD3D3E1AB, 0x21B862A8, 0x32E8915C, 0xC083125F,
132 0x144976B4, 0xE622F5B7, 0xF5720643, 0x07198540,
133 0x590AB964, 0xAB613A67, 0xB831C993, 0x4A5A4A90,
134 0x9E902E7B, 0x6CFBAD78, 0x7FAB5E8C, 0x8DC0DD8F,
135 0xE330A81A, 0x115B2B19, 0x020BD8ED, 0xF0605BEE,
136 0x24AA3F05, 0xD6C1BC06, 0xC5914FF2, 0x37FACCF1,
137 0x69E9F0D5, 0x9B8273D6, 0x88D28022, 0x7AB90321,
138 0xAE7367CA, 0x5C18E4C9, 0x4F48173D, 0xBD23943E,
139 0xF36E6F75, 0x0105EC76, 0x12551F82, 0xE03E9C81,
140 0x34F4F86A, 0xC69F7B69, 0xD5CF889D, 0x27A40B9E,
141 0x79B737BA, 0x8BDCB4B9, 0x988C474D, 0x6AE7C44E,
142 0xBE2DA0A5, 0x4C4623A6, 0x5F16D052, 0xAD7D5351 }
145 local function CRC8(crc, d)
146 return bit.bxor(bit.rshift(crc, 8), crc32c_table[bit.band(0xff, bit.bxor(crc, d))])
149 local function CRC16(crc, d)
150 crc = CRC8(crc, bit.band(d, 0xFF))
152 crc = CRC8(crc, bit.band(d, 0xFF))
156 local function string_crc(str, crc)
158 -- print("S", i, string.byte(str, i), string.char(string.byte(str, i)))
159 crc = CRC8(crc, string.byte(str, i))
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 } },
202 function vpp.crc_version_string(data)
204 -- Get rid of comments
205 data = data:gsub("/%*.-%*/", "")
206 data = data:gsub("//[^\n]+", "")
211 for k, v in ipairs(tokens) do
213 local x, y, cap = string.find(data, v["match"], idx)
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] )
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)
229 -- print(vpp.dump(matched))
232 -- print("NOT MATCHED!")
233 local crc = CRC16(input_crc, 0xFFFFFFFF)
234 return string.sub(string.format("%x", crc), -8)
241 if type(o) == 'table' then
243 for k,v in pairs(o) do
244 if type(k) ~= 'number' then k = '"'..k..'"' end
245 s = s .. '['..k..'] = ' .. vpp.dump(v) .. ','
253 function vpp.hex_dump(buf)
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
261 return table.concat(ret)
265 function vpp.c_str(text_in)
266 local text = text_in -- \000 will be helpfully added by ffi.copy
267 local c_str = ffi.new("char[?]", #text+1)
268 ffi.copy(c_str, text)
273 function vpp.init(vpp, args)
274 local pneum_api = args.pneum_api or [[
275 int cough_pneum_attach(char *pneum_path, char *cough_path);
276 int pneum_connect(char *name, char *chroot_prefix, void *cb);
277 int pneum_disconnect(void);
278 int pneum_read(char **data, int *l);
279 int pneum_write(char *data, int len);
280 void pneum_free(char *data);
283 vpp.pneum_path = args.pneum_path
286 vpp.pneum = ffi.load(vpp.pneum_path)
287 if (init_res < 0) then
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 = {}
299 vpp.plugin_version = {}
300 vpp.is_connected = false
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)
311 elseif type(src) == "table" then
312 for i,v in ipairs(src) do
313 ffi.cast("u8 *", dst_c_ptr)[i-1] = v
317 return 1, src -- ffi.cast("u8", src)
320 vpp.t_c2lua["u8"] = function(c_type, src_ptr, src_len)
322 return ffi.string(src_ptr, src_len)
324 return (tonumber(src_ptr))
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)
335 return 2, (ffi.C.htons(src))
338 vpp.t_c2lua["u16"] = function(c_type, src_ptr, src_len)
341 for i = 0,src_len-1 do
342 out[i+1] = tonumber(ffi.C.ntohs(src_ptr[i]))
346 return (tonumber(ffi.C.ntohs(src_ptr)))
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)
357 return 4, (ffi.C.htonl(src))
360 vpp.t_c2lua["u32"] = function(c_type, src_ptr, src_len)
363 for i = 0,src_len-1 do
364 out[i+1] = tonumber(ffi.C.ntohl(src_ptr[i]))
368 return (tonumber(ffi.C.ntohl(src_ptr)))
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)
378 return 4, (ffi.C.htonl(src))
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))
390 for i = 0,src_len-1 do
391 out[i+1] = tonumber(ntohl(src_ptr[i]))
394 return (tonumber(ntohl(src_ptr)))
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
405 return 8, ffi.cast("u64", src) --- FIXME ENDIAN
408 vpp.t_c2lua["u64"] = function(c_type, src_ptr, src_len)
411 for i = 0,src_len-1 do
412 out[i+1] = tonumber(src_ptr[i]) -- FIXME ENDIAN
416 return (tonumber(src_ptr)) --FIXME ENDIAN
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]
434 print("ERROR: field " .. tostring(k) .. " in message " .. tostring(c_type) .. " is unknown")
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
443 print("__MSG__ " .. tostring(c_type) .. " t_lua2c: can not store field " .. field.name ..
444 " type " .. field.c_type .. " dst " .. tostring(dst[k]))
449 if field.array and (type(v) == "table") then
450 -- print("NTFY: field " .. tostring(k) .. " in message " .. tostring(c_type) .. " is an array")
451 for field_i, field_v in ipairs(v) do
452 -- print("NTFY: setting member#" .. tostring(field_i) .. " to value " .. vpp.dump(field_v))
453 local field_len, field_val = lua2c(field.c_type, field_v, dst[k][field_i-1])
454 len = len + field_len
457 len, val = lua2c(field.c_type, v, dst[k])
459 if not field.array then
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]
470 dst[field.array_size] = vpp.t_c2lua[size_field.c_type](size_field.c_type, len)
475 -- print("Full message:\n" .. vpp.hex_dump(ffi.string(ffi.cast('void *', req_store_cache), 64)))
477 return (ffi.sizeof(dst[0])+additional_len)
480 vpp.t_c2lua["__MSG__"] = function(c_type, src_ptr, src_len)
482 local reply_typed_ptr = ffi.cast(c_type .. " *", src_ptr)
483 local field_desc = vpp.c_type_to_fields[c_type]
485 for i = 0,src_len-1 do
486 out[i+1] = vpp.t_c2lua[c_type](c_type, src_ptr[i])
491 for k, v in pairs(field_desc) do
492 local v_c2lua = vpp.t_c2lua[v.c_type]
497 local len_field_name = k .. "_length"
498 local len_field = field_desc[len_field_name]
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)
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)
509 -- alas, just stuff the entire array
510 out[k] = v_c2lua(v.c_type, reply_typed_ptr[k], len)
513 out[k] = v_c2lua(v.c_type, reply_typed_ptr[k])
516 out[k] = "<no accessor function for type " .. tostring(v.c_type) .. ">"
526 function vpp.connect(vpp, client_name)
527 local name = "lua_client"
531 local ret = vpp.pneum.pneum_connect(vpp.c_str(client_name), nil, nil)
532 if tonumber(ret) == 0 then
533 vpp.is_connected = true
537 function vpp.disconnect(vpp)
538 vpp.pneum.pneum_disconnect()
541 function vpp.consume_api(vpp, path, plugin_name)
542 -- print("Consuming the VPP api from "..path)
544 local f = io.open(path, "r")
546 print("Could not open " .. path)
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!")
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))
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;"
573 local c_type = "vl_api_" .. name .. "_t"
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
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
596 fieldcountvar = cnt:sub(2, -2)
600 local fieldrec = { name = fieldname, c_type = fieldtype, array = fieldcount, array_size = fieldcountvar }
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!")
607 print("Unknown array type: ", name, " : " , fieldname, " : ", fieldtype, ":", fieldcount, ":", fieldcountvar)
610 fields[fieldname] = fieldrec
613 -- print(dump(fields))
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"
624 table.insert(ffii, onedef);
627 local cdef = table.concat(ffii)
633 function vpp.lua2c(vpp, c_type, src, dst_c_ptr)
634 -- returns the number of bytes written to memory pointed by dst
635 local lua2c = vpp.t_lua2c[c_type]
637 return(lua2c(c_type, src, dst_c_ptr))
639 print("vpp.lua2c: do not know how to store type " .. c_type)
644 function vpp.c2lua(vpp, c_type, src_ptr, src_len)
645 -- returns the lua data structure
646 local c2lua = vpp.t_c2lua[c_type]
648 return(c2lua(c_type, src_ptr, src_len))
650 print("vpp.c2lua: do not know how to load type " .. c_type)
655 local req_store_cache = ffi.new("vl_api_opaque_message_t[1]")
657 function vpp.api_write(vpp, api_name, req_table)
658 local msg_num = vpp.msg_name_to_number[api_name]
660 print ("API call "..api_name.." is not known")
664 if not req_table then
667 req_table._vl_msg_id = msg_num
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)))
674 res = vpp.pneum.pneum_write(ffi.cast('void *', req_store_cache), packed_len)
678 local rep_store_cache = ffi.new("vl_api_opaque_message_t *[1]")
679 local rep_len_cache = ffi.new("int[1]")
681 function vpp.api_read(vpp)
682 local rep_type = "vl_api_opaque_message_t"
683 local rep = rep_store_cache
684 local replen = rep_len_cache
685 res = vpp.pneum.pneum_read(ffi.cast("void *", rep), replen)
686 if vpp.debug_dump then
687 print("Read Message length: " .. tostring(replen[0]) .. "\n" .. vpp.hex_dump(ffi.string(ffi.cast('void *', rep[0]), replen[0])))
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]
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
699 vpp.pneum.pneum_free(ffi.cast('void *',rep[0]))
701 return reply_msg_name, out
704 function vpp.api_call(vpp, api_name, req_table, options_in)
705 local msg_num = vpp.msg_name_to_number[api_name]
706 local end_message_name = api_name .."_reply"
709 local options = options_in or {}
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")
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)
722 if reply_message_name ~= "control_ping_reply" then
723 -- do not insert the control ping encapsulation
724 table.insert(replies, reply)
728 until reply_message_name == end_message_name
730 print(api_name .. " is an unknown API call")