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);
277 int pneum_connect_sync(char *name, char *chroot_prefix);
278 int pneum_disconnect(void);
279 int pneum_read(char **data, int *l);
280 int pneum_write(char *data, int len);
282 void pneum_data_free(char *data);
285 vpp.pneum_path = args.pneum_path
289 vpp.cough_path = args.cough_path or "./libcough.so"
290 vpp.cough = ffi.load(vpp.cough_path)
293 if(vpp.cough.cough_pneum_attach) then
294 vpp.pneum_is_cough = true
295 print("libcough detected\n")
296 init_res = vpp.cough.cough_pneum_attach(vpp.c_str(vpp.pneum_path), vpp.c_str(vpp.cough_path))
297 vpp.pneum = vpp.cough
301 vpp.pneum = ffi.load(vpp.pneum_path)
303 if (init_res < 0) then
308 vpp.msg_name_to_number = {}
309 vpp.msg_name_to_fields = {}
310 vpp.msg_number_to_name = {}
311 vpp.msg_number_to_type = {}
312 vpp.msg_number_to_pointer_type = {}
313 vpp.c_type_to_fields = {}
315 vpp.plugin_version = {}
316 vpp.is_connected = false
321 vpp.t_lua2c["u8"] = function(c_type, src, dst_c_ptr)
322 if type(src) == "string" then
323 -- ffi.copy adds a zero byte at the end. Grrr.
324 -- ffi.copy(dst_c_ptr, src)
325 ffi.C.memcpy(dst_c_ptr, vpp.c_str(src), #src)
327 elseif type(src) == "table" then
328 for i,v in ipairs(src) do
329 ffi.cast("u8 *", dst_c_ptr)[i-1] = v
333 return 1, src -- ffi.cast("u8", src)
336 vpp.t_c2lua["u8"] = function(c_type, src_ptr, src_len)
338 return ffi.string(src_ptr, src_len)
340 return (tonumber(src_ptr))
344 vpp.t_lua2c["u16"] = function(c_type, src, dst_c_ptr)
345 if type(src) == "table" then
346 for i,v in ipairs(src) do
347 ffi.cast("u16 *", dst_c_ptr)[i-1] = ffi.C.htons(v)
351 return 2, (ffi.C.htons(src))
354 vpp.t_c2lua["u16"] = function(c_type, src_ptr, src_len)
357 for i = 0,src_len-1 do
358 out[i+1] = tonumber(ffi.C.ntohs(src_ptr[i]))
362 return (tonumber(ffi.C.ntohs(src_ptr)))
366 vpp.t_lua2c["u32"] = function(c_type, src, dst_c_ptr)
367 if type(src) == "table" then
368 for i,v in ipairs(src) do
369 ffi.cast("u32 *", dst_c_ptr)[i-1] = ffi.C.htonl(v)
373 return 4, (ffi.C.htonl(src))
376 vpp.t_c2lua["u32"] = function(c_type, src_ptr, src_len)
379 for i = 0,src_len-1 do
380 out[i+1] = tonumber(ffi.C.ntohl(src_ptr[i]))
384 return (tonumber(ffi.C.ntohl(src_ptr)))
387 vpp.t_lua2c["i32"] = function(c_type, src, dst_c_ptr)
388 if type(src) == "table" then
389 for i,v in ipairs(src) do
390 ffi.cast("i32 *", dst_c_ptr)[i-1] = ffi.C.htonl(v)
394 return 4, (ffi.C.htonl(src))
397 vpp.t_c2lua["i32"] = function(c_type, src_ptr, src_len)
398 local ntohl = function(src)
399 local u32val = ffi.cast("u32", src)
400 local ntohlval = (ffi.C.ntohl(u32val))
401 local out = tonumber(ffi.cast("i32", ntohlval + 0LL))
406 for i = 0,src_len-1 do
407 out[i+1] = tonumber(ntohl(src_ptr[i]))
410 return (tonumber(ntohl(src_ptr)))
414 vpp.t_lua2c["u64"] = function(c_type, src, dst_c_ptr)
415 if type(src) == "table" then
416 for i,v in ipairs(src) do
417 ffi.cast("u64 *", dst_c_ptr)[i-1] = v --- FIXME ENDIAN
421 return 8, ffi.cast("u64", src) --- FIXME ENDIAN
424 vpp.t_c2lua["u64"] = function(c_type, src_ptr, src_len)
427 for i = 0,src_len-1 do
428 out[i+1] = tonumber(src_ptr[i]) -- FIXME ENDIAN
432 return (tonumber(src_ptr)) --FIXME ENDIAN
439 vpp.t_lua2c["__MSG__"] = function(c_type, src, dst_c_ptr)
440 local dst = ffi.cast(c_type .. " *", dst_c_ptr)
441 local additional_len = 0
442 local fields_info = vpp.c_type_to_fields[c_type]
443 -- print("__MSG__ type: " .. tostring(c_type))
444 ffi.C.memset(dst_c_ptr, 0, ffi.sizeof(dst[0]))
445 -- print(vpp.dump(fields_info))
446 -- print(vpp.dump(src))
447 for k,v in pairs(src) do
448 local field = fields_info[k]
450 print("ERROR: field " .. tostring(k) .. " in message " .. tostring(c_type) .. " is unknown")
452 local lua2c = vpp.t_lua2c[field.c_type]
453 -- print("__MSG__ field " .. tostring(k) .. " : " .. vpp.dump(field))
454 -- if the field is not an array type, try to coerce the argument to a number
455 if not field.array and type(v) == "string" then
459 print("__MSG__ " .. tostring(c_type) .. " t_lua2c: can not store field " .. field.name ..
460 " type " .. field.c_type .. " dst " .. tostring(dst[k]))
465 if field.array and (type(v) == "table") then
466 print("NTFY: field " .. tostring(k) .. " in message " .. tostring(c_type) .. " is an array")
467 for field_i, field_v in ipairs(v) do
468 print("NTFY: setting member#" .. tostring(field_i) .. " to value " .. vpp.dump(field_v))
469 local field_len, field_val = lua2c(field.c_type, field_v, dst[k][field_i-1])
470 len = len + field_len
473 len, val = lua2c(field.c_type, v, dst[k])
475 if not field.array then
478 if 0 == field.array then
479 additional_len = additional_len + len
480 -- print("Adding " .. tostring(len) .. " bytes due to field " .. tostring(field.name))
481 -- If there is a variable storing the length
482 -- and the input table does not set it, do magic
483 if field.array_size and not src[field.array_size] then
484 local size_field = fields_info[field.array_size]
486 dst[field.array_size] = vpp.t_c2lua[size_field.c_type](size_field.c_type, len)
491 -- print("Full message:\n" .. vpp.hex_dump(ffi.string(ffi.cast('void *', req_store_cache), 64)))
493 return (ffi.sizeof(dst[0])+additional_len)
496 vpp.t_c2lua["__MSG__"] = function(c_type, src_ptr, src_len)
498 local reply_typed_ptr = ffi.cast(c_type .. " *", src_ptr)
499 local field_desc = vpp.c_type_to_fields[c_type]
501 for i = 0,src_len-1 do
502 out[i+1] = vpp.t_c2lua[c_type](c_type, src_ptr[i])
507 for k, v in pairs(field_desc) do
508 local v_c2lua = vpp.t_c2lua[v.c_type]
513 local len_field_name = k .. "_length"
514 local len_field = field_desc[len_field_name]
516 local real_len = vpp.t_c2lua[len_field.c_type](len_field.c_type, reply_typed_ptr[len_field_name])
517 out[k] = v_c2lua(v.c_type, reply_typed_ptr[k], real_len)
519 -- check if len = 0, then must be a field which contains the size
520 len_field = field_desc[v.array_size]
521 local real_len = vpp.t_c2lua[len_field.c_type](len_field.c_type, reply_typed_ptr[v.array_size])
522 -- print("REAL length: " .. vpp.dump(v) .. " : " .. tostring(real_len))
523 out[k] = v_c2lua(v.c_type, reply_typed_ptr[k], real_len)
525 -- alas, just stuff the entire array
526 out[k] = v_c2lua(v.c_type, reply_typed_ptr[k], len)
529 out[k] = v_c2lua(v.c_type, reply_typed_ptr[k])
532 out[k] = "<no accessor function for type " .. tostring(v.c_type) .. ">"
542 function vpp.connect(vpp, client_name)
543 local name = "lua_client"
547 local ret = vpp.pneum.pneum_connect_sync(vpp.c_str(client_name), nil)
548 if tonumber(ret) == 0 then
549 vpp.is_connected = true
553 function vpp.disconnect(vpp)
554 vpp.pneum.pneum_disconnect()
557 function vpp.consume_api(vpp, path, plugin_name)
558 -- print("Consuming the VPP api from "..path)
560 local f = io.open(path, "r")
562 print("Could not open " .. path)
565 local data = f:read("*all")
566 -- Remove all C comments
567 data = data:gsub("/%*.-%*/", "")
568 if vpp.is_connected and not plugin_name then
569 print(path .. ": must specify plugin name!")
573 vpp.plugin_version[plugin_name] = vpp.crc_version_string(data)
574 local full_plugin_name = plugin_name .. "_" .. vpp.plugin_version[plugin_name]
575 local reply = vpp:api_call("get_first_msg_id", { name = full_plugin_name } )
576 vpp.next_msg_num = tonumber(reply[1].first_msg_id)
577 print("Plugin " .. full_plugin_name .. " first message is " .. tostring(vpp.next_msg_num))
579 -- print ("data len: ", #data)
580 data = data:gsub("\n(.-)(%S+)%s*{([^}]*)}", function (preamble, name, members)
581 local _, typeonly = preamble:gsub("typeonly", "")
582 local maybe_msg_id_field = { [0] = "u16 _vl_msg_id;", "" }
583 local onedef = "\n\n#pragma pack(1)\ntypedef struct _vl_api_"..name.. " {\n" ..
584 -- " u16 _vl_msg_id;" ..
585 maybe_msg_id_field[typeonly] ..
586 members:gsub("%[[a-zA-Z_]+]", "[0]") ..
587 "} vl_api_" .. name .. "_t;"
589 local c_type = "vl_api_" .. name .. "_t"
592 -- vpp.msg_name_to_fields[name] = fields
593 -- print("CTYPE " .. c_type)
594 vpp.c_type_to_fields[c_type] = fields
595 vpp.t_lua2c[c_type] = vpp.t_lua2c["__MSG__"]
596 vpp.t_c2lua[c_type] = vpp.t_c2lua["__MSG__"]
597 local mirec = { name = "_vl_msg_id", c_type = "u16", array = nil, array_size = nil }
598 if typeonly == 0 then
599 fields[mirec.name] = mirec
602 -- populate the field reflection table for the message
603 -- sets the various type information as well as the accessors for lua<->C conversion
604 members:gsub("(%S+)%s+(%S+);", function (fieldtype, fieldname)
605 local fieldcount = nil
606 local fieldcountvar = nil
607 -- data = data:gsub("%[[a-zA-Z_]+]", "[0]")
608 fieldname = fieldname:gsub("(%b[])", function(cnt)
609 fieldcount = tonumber(cnt:sub(2, -2));
610 if not fieldcount then
612 fieldcountvar = cnt:sub(2, -2)
616 local fieldrec = { name = fieldname, c_type = fieldtype, array = fieldcount, array_size = fieldcountvar }
618 if fieldtype == "u8" then
619 -- any array of bytes is treated as a string
620 elseif vpp.t_lua2c[fieldtype] then
621 -- print("Array of " .. fieldtype .. " is ok!")
623 print("Unknown array type: ", name, " : " , fieldname, " : ", fieldtype, ":", fieldcount, ":", fieldcountvar)
626 fields[fieldname] = fieldrec
629 -- print(dump(fields))
631 if typeonly == 0 then
632 local this_message_number = vpp.next_msg_num
633 vpp.next_msg_num = vpp.next_msg_num + 1
634 vpp.msg_name_to_number[name] = this_message_number
635 vpp.msg_number_to_name[this_message_number] = name
636 vpp.msg_number_to_type[this_message_number] = "vl_api_" .. name .. "_t"
637 vpp.msg_number_to_pointer_type[this_message_number] = vpp.msg_number_to_type[this_message_number] .. " *"
638 onedef = onedef .. "\n\n enum { vl_msg_" .. name .. " = " .. this_message_number .. " };\n\n"
640 table.insert(ffii, onedef);
643 local cdef = table.concat(ffii)
649 function vpp.lua2c(vpp, c_type, src, dst_c_ptr)
650 -- returns the number of bytes written to memory pointed by dst
651 local lua2c = vpp.t_lua2c[c_type]
653 return(lua2c(c_type, src, dst_c_ptr))
655 print("vpp.lua2c: do not know how to store type " .. c_type)
660 function vpp.c2lua(vpp, c_type, src_ptr, src_len)
661 -- returns the lua data structure
662 local c2lua = vpp.t_c2lua[c_type]
664 return(c2lua(c_type, src_ptr, src_len))
666 print("vpp.c2lua: do not know how to load type " .. c_type)
671 local req_store_cache = ffi.new("vl_api_opaque_message_t[1]")
673 function vpp.api_write(vpp, api_name, req_table)
674 local msg_num = vpp.msg_name_to_number[api_name]
676 print ("API call "..api_name.." is not known")
680 if not req_table then
683 req_table._vl_msg_id = msg_num
685 local packed_len = vpp:lua2c(vpp.msg_number_to_type[msg_num], req_table, req_store_cache)
686 if vpp.debug_dump then
687 print("Write Message length: " .. tostring(packed_len) .. "\n" .. vpp.hex_dump(ffi.string(ffi.cast('void *', req_store_cache), packed_len)))
690 res = vpp.pneum.pneum_write(ffi.cast('void *', req_store_cache), packed_len)
694 local rep_store_cache = ffi.new("vl_api_opaque_message_t *[1]")
695 local rep_len_cache = ffi.new("int[1]")
697 function vpp.api_read(vpp)
698 local rep_type = "vl_api_opaque_message_t"
699 local rep = rep_store_cache
700 local replen = rep_len_cache
701 res = vpp.pneum.pneum_read(ffi.cast("void *", rep), replen)
702 if vpp.debug_dump then
703 print("Read Message length: " .. tostring(replen[0]) .. "\n" .. vpp.hex_dump(ffi.string(ffi.cast('void *', rep[0]), replen[0])))
706 local reply_msg_num = ffi.C.ntohs(rep[0]._vl_msg_id)
707 local reply_msg_name = vpp.msg_number_to_name[reply_msg_num]
709 local reply_typed_ptr = ffi.cast(vpp.msg_number_to_pointer_type[reply_msg_num], rep[0])
710 local out = vpp:c2lua(vpp.msg_number_to_type[reply_msg_num], rep[0], nil, replen[0])
711 if type(out) == "table" then
712 out["luaapi_message_name"] = reply_msg_name
715 vpp.pneum.pneum_data_free(ffi.cast('void *',rep[0]))
717 return reply_msg_name, out
720 function vpp.api_call(vpp, api_name, req_table, options_in)
721 local msg_num = vpp.msg_name_to_number[api_name]
722 local end_message_name = api_name .."_reply"
725 local options = options_in or {}
727 vpp:api_write(api_name, req_table)
728 if not vpp.msg_name_to_number[end_message_name] or options.force_ping then
729 end_message_name = "control_ping_reply"
730 vpp:api_write("control_ping")
733 reply_message_name, reply = vpp:api_read()
734 if reply and not reply.context then
735 -- there may be async events inbetween
736 table.insert(vpp.events, reply)
738 if reply_message_name ~= "control_ping_reply" then
739 -- do not insert the control ping encapsulation
740 table.insert(replies, reply)
744 until reply_message_name == end_message_name
746 print(api_name .. " is an unknown API call")