Andrew Yourtchenko | fa1456a | 2016-11-11 16:32:52 +0000 | [diff] [blame] | 1 | --[[ |
| 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 | |
| 18 | local vpp = {} |
| 19 | |
| 20 | local ffi = require("ffi") |
| 21 | |
| 22 | --[[ |
| 23 | |
| 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. |
| 28 | |
| 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. |
| 32 | |
| 33 | ]] |
| 34 | |
| 35 | |
| 36 | ffi.cdef([[ |
| 37 | |
| 38 | typedef uint8_t u8; |
| 39 | typedef int8_t i8; |
| 40 | typedef uint16_t u16; |
| 41 | typedef int16_t i16; |
| 42 | typedef uint32_t u32; |
| 43 | typedef int32_t i32; |
| 44 | typedef uint64_t u64; |
| 45 | typedef int64_t i64; |
| 46 | typedef double f64; |
| 47 | typedef float f32; |
| 48 | |
| 49 | #pragma pack(1) |
| 50 | typedef union { |
| 51 | u16 u16; |
| 52 | i16 i16; |
| 53 | } lua_ui16t; |
| 54 | |
| 55 | #pragma pack(1) |
| 56 | typedef union { |
| 57 | u32 u32; |
| 58 | i32 i32; |
| 59 | } lua_ui32t; |
| 60 | |
| 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); |
| 67 | |
| 68 | #pragma pack(1) |
| 69 | typedef 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 | |
| 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 } |
| 143 | ); |
| 144 | |
| 145 | local function CRC8(crc, d) |
| 146 | return bit.bxor(bit.rshift(crc, 8), crc32c_table[bit.band(0xff, bit.bxor(crc, d))]) |
| 147 | end |
| 148 | |
| 149 | local 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 |
| 154 | end |
| 155 | |
| 156 | local 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 |
| 162 | end |
| 163 | |
| 164 | local 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 | |
| 202 | function 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 |
| 237 | end |
| 238 | |
| 239 | |
| 240 | function 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 |
| 251 | end |
| 252 | |
| 253 | function 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) |
| 262 | end |
| 263 | |
| 264 | |
| 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) |
| 269 | return c_str |
| 270 | end |
| 271 | |
| 272 | |
| 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); |
| 281 | |
| 282 | void pneum_data_free(char *data); |
| 283 | ]] |
| 284 | |
| 285 | vpp.pneum_path = args.pneum_path |
| 286 | ffi.cdef(pneum_api) |
| 287 | local init_res = 0 |
| 288 | if pcall(function() |
| 289 | vpp.cough_path = args.cough_path or "./libcough.so" |
| 290 | vpp.cough = ffi.load(vpp.cough_path) |
| 291 | end) then |
| 292 | pcall(function() |
| 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 |
| 298 | end |
| 299 | end) |
| 300 | else |
| 301 | vpp.pneum = ffi.load(vpp.pneum_path) |
| 302 | end |
| 303 | if (init_res < 0) then |
| 304 | return nil |
| 305 | end |
| 306 | |
| 307 | vpp.next_msg_num = 1 |
| 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 = {} |
| 314 | vpp.events = {} |
| 315 | vpp.plugin_version = {} |
| 316 | vpp.is_connected = false |
| 317 | |
| 318 | |
| 319 | vpp.t_lua2c = {} |
| 320 | vpp.t_c2lua = {} |
| 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) |
| 326 | return(#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 |
| 330 | end |
| 331 | return(#src) |
| 332 | else |
| 333 | return 1, src -- ffi.cast("u8", src) |
| 334 | end |
| 335 | end |
| 336 | vpp.t_c2lua["u8"] = function(c_type, src_ptr, src_len) |
| 337 | if src_len then |
| 338 | return ffi.string(src_ptr, src_len) |
| 339 | else |
| 340 | return (tonumber(src_ptr)) |
| 341 | end |
| 342 | end |
| 343 | |
| 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) |
| 348 | end |
| 349 | return(2 * #src) |
| 350 | else |
| 351 | return 2, (ffi.C.htons(src)) |
| 352 | end |
| 353 | end |
| 354 | vpp.t_c2lua["u16"] = function(c_type, src_ptr, src_len) |
| 355 | if src_len then |
| 356 | local out = {} |
| 357 | for i = 0,src_len-1 do |
| 358 | out[i+1] = tonumber(ffi.C.ntohs(src_ptr[i])) |
| 359 | end |
| 360 | return out |
| 361 | else |
| 362 | return (tonumber(ffi.C.ntohs(src_ptr))) |
| 363 | end |
| 364 | end |
| 365 | |
| 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) |
| 370 | end |
| 371 | return(4 * #src) |
| 372 | else |
| 373 | return 4, (ffi.C.htonl(src)) |
| 374 | end |
| 375 | end |
| 376 | vpp.t_c2lua["u32"] = function(c_type, src_ptr, src_len) |
| 377 | if src_len then |
| 378 | local out = {} |
| 379 | for i = 0,src_len-1 do |
| 380 | out[i+1] = tonumber(ffi.C.ntohl(src_ptr[i])) |
| 381 | end |
| 382 | return out |
| 383 | else |
| 384 | return (tonumber(ffi.C.ntohl(src_ptr))) |
| 385 | end |
| 386 | end |
| 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) |
| 391 | end |
| 392 | return(4 * #src) |
| 393 | else |
| 394 | return 4, (ffi.C.htonl(src)) |
| 395 | end |
| 396 | end |
| 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)) |
| 402 | return out |
| 403 | end |
| 404 | if src_len then |
| 405 | local out = {} |
| 406 | for i = 0,src_len-1 do |
| 407 | out[i+1] = tonumber(ntohl(src_ptr[i])) |
| 408 | end |
| 409 | else |
| 410 | return (tonumber(ntohl(src_ptr))) |
| 411 | end |
| 412 | end |
| 413 | |
| 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 |
| 418 | end |
| 419 | return(8 * #src) |
| 420 | else |
| 421 | return 8, ffi.cast("u64", src) --- FIXME ENDIAN |
| 422 | end |
| 423 | end |
| 424 | vpp.t_c2lua["u64"] = function(c_type, src_ptr, src_len) |
| 425 | if src_len then |
| 426 | local out = {} |
| 427 | for i = 0,src_len-1 do |
| 428 | out[i+1] = tonumber(src_ptr[i]) -- FIXME ENDIAN |
| 429 | end |
| 430 | return out |
| 431 | else |
| 432 | return (tonumber(src_ptr)) --FIXME ENDIAN |
| 433 | end |
| 434 | end |
| 435 | |
| 436 | |
| 437 | |
| 438 | |
| 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] |
| 449 | if not field then |
| 450 | print("ERROR: field " .. tostring(k) .. " in message " .. tostring(c_type) .. " is unknown") |
| 451 | end |
| 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 |
| 456 | v = tonumber(v) |
| 457 | end |
| 458 | if not lua2c then |
| 459 | print("__MSG__ " .. tostring(c_type) .. " t_lua2c: can not store field " .. field.name .. |
| 460 | " type " .. field.c_type .. " dst " .. tostring(dst[k])) |
| 461 | return 0 |
| 462 | end |
| 463 | local len = 0 |
| 464 | local val = nil |
| 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 |
| 471 | end |
| 472 | else |
| 473 | len, val = lua2c(field.c_type, v, dst[k]) |
| 474 | end |
| 475 | if not field.array then |
| 476 | dst[k] = val |
| 477 | else |
| 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] |
| 485 | if size_field then |
| 486 | dst[field.array_size] = vpp.t_c2lua[size_field.c_type](size_field.c_type, len) |
| 487 | end |
| 488 | end |
| 489 | end |
| 490 | end |
| 491 | -- print("Full message:\n" .. vpp.hex_dump(ffi.string(ffi.cast('void *', req_store_cache), 64))) |
| 492 | end |
| 493 | return (ffi.sizeof(dst[0])+additional_len) |
| 494 | end |
| 495 | |
| 496 | vpp.t_c2lua["__MSG__"] = function(c_type, src_ptr, src_len) |
| 497 | local out = {} |
| 498 | local reply_typed_ptr = ffi.cast(c_type .. " *", src_ptr) |
| 499 | local field_desc = vpp.c_type_to_fields[c_type] |
| 500 | if src_len then |
| 501 | for i = 0,src_len-1 do |
| 502 | out[i+1] = vpp.t_c2lua[c_type](c_type, src_ptr[i]) |
| 503 | end |
| 504 | return out |
| 505 | end |
| 506 | |
| 507 | for k, v in pairs(field_desc) do |
| 508 | local v_c2lua = vpp.t_c2lua[v.c_type] |
| 509 | if v_c2lua then |
| 510 | local len = v.array |
| 511 | -- print(dump(v)) |
| 512 | if len then |
| 513 | local len_field_name = k .. "_length" |
| 514 | local len_field = field_desc[len_field_name] |
| 515 | if (len_field) then |
| 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) |
| 518 | elseif len == 0 then |
| 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) |
| 524 | else |
| 525 | -- alas, just stuff the entire array |
| 526 | out[k] = v_c2lua(v.c_type, reply_typed_ptr[k], len) |
| 527 | end |
| 528 | else |
| 529 | out[k] = v_c2lua(v.c_type, reply_typed_ptr[k]) |
| 530 | end |
| 531 | else |
| 532 | out[k] = "<no accessor function for type " .. tostring(v.c_type) .. ">" |
| 533 | end |
| 534 | -- print(k, out[k]) |
| 535 | end |
| 536 | return out |
| 537 | end |
| 538 | |
| 539 | return vpp |
| 540 | end |
| 541 | |
| 542 | function vpp.connect(vpp, client_name) |
| 543 | local name = "lua_client" |
| 544 | if client_name then |
| 545 | name = client_name |
| 546 | end |
| 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 |
| 550 | end |
| 551 | end |
| 552 | |
| 553 | function vpp.disconnect(vpp) |
| 554 | vpp.pneum.pneum_disconnect() |
| 555 | end |
| 556 | |
| 557 | function vpp.consume_api(vpp, path, plugin_name) |
| 558 | -- print("Consuming the VPP api from "..path) |
| 559 | local ffii = {} |
| 560 | local f = io.open(path, "r") |
| 561 | if not f then |
| 562 | print("Could not open " .. path) |
| 563 | return nil |
| 564 | end |
| 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!") |
| 570 | return |
| 571 | end |
| 572 | if plugin_name then |
| 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)) |
| 578 | end |
| 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;" |
| 588 | |
| 589 | local c_type = "vl_api_" .. name .. "_t" |
| 590 | |
| 591 | local fields = {} |
| 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 |
| 600 | end |
| 601 | |
| 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 |
| 611 | fieldcount = 0 |
| 612 | fieldcountvar = cnt:sub(2, -2) |
| 613 | end |
| 614 | return "" |
| 615 | end) |
| 616 | local fieldrec = { name = fieldname, c_type = fieldtype, array = fieldcount, array_size = fieldcountvar } |
| 617 | if fieldcount then |
| 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!") |
| 622 | else |
| 623 | print("Unknown array type: ", name, " : " , fieldname, " : ", fieldtype, ":", fieldcount, ":", fieldcountvar) |
| 624 | end |
| 625 | end |
| 626 | fields[fieldname] = fieldrec |
| 627 | end) |
| 628 | |
| 629 | -- print(dump(fields)) |
| 630 | |
| 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" |
| 639 | end |
| 640 | table.insert(ffii, onedef); |
| 641 | return ""; |
| 642 | end) |
| 643 | local cdef = table.concat(ffii) |
| 644 | -- print(cdef) |
| 645 | ffi.cdef(cdef) |
| 646 | end |
| 647 | |
| 648 | |
| 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] |
| 652 | if lua2c then |
| 653 | return(lua2c(c_type, src, dst_c_ptr)) |
| 654 | else |
| 655 | print("vpp.lua2c: do not know how to store type " .. c_type) |
| 656 | return 0 |
| 657 | end |
| 658 | end |
| 659 | |
| 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] |
| 663 | if c2lua then |
| 664 | return(c2lua(c_type, src_ptr, src_len)) |
| 665 | else |
| 666 | print("vpp.c2lua: do not know how to load type " .. c_type) |
| 667 | return nil |
| 668 | end |
| 669 | end |
| 670 | |
| 671 | local req_store_cache = ffi.new("vl_api_opaque_message_t[1]") |
| 672 | |
| 673 | function vpp.api_write(vpp, api_name, req_table) |
| 674 | local msg_num = vpp.msg_name_to_number[api_name] |
| 675 | if not msg_num then |
| 676 | print ("API call "..api_name.." is not known") |
| 677 | return nil |
| 678 | end |
| 679 | |
| 680 | if not req_table then |
| 681 | req_table = {} |
| 682 | end |
| 683 | req_table._vl_msg_id = msg_num |
| 684 | |
| 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))) |
| 688 | end |
| 689 | |
| 690 | res = vpp.pneum.pneum_write(ffi.cast('void *', req_store_cache), packed_len) |
| 691 | return res |
| 692 | end |
| 693 | |
| 694 | local rep_store_cache = ffi.new("vl_api_opaque_message_t *[1]") |
| 695 | local rep_len_cache = ffi.new("int[1]") |
| 696 | |
| 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]))) |
| 704 | end |
| 705 | |
| 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] |
| 708 | |
| 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 |
| 713 | end |
| 714 | |
| 715 | vpp.pneum.pneum_data_free(ffi.cast('void *',rep[0])) |
| 716 | |
| 717 | return reply_msg_name, out |
| 718 | end |
| 719 | |
| 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" |
| 723 | local replies = {} |
| 724 | local cstruct = "" |
| 725 | local options = options_in or {} |
| 726 | if msg_num then |
| 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") |
| 731 | end |
| 732 | repeat |
| 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) |
| 737 | else |
| 738 | if reply_message_name ~= "control_ping_reply" then |
| 739 | -- do not insert the control ping encapsulation |
| 740 | table.insert(replies, reply) |
| 741 | end |
| 742 | end |
| 743 | -- print(reply) |
| 744 | until reply_message_name == end_message_name |
| 745 | else |
| 746 | print(api_name .. " is an unknown API call") |
| 747 | return nil |
| 748 | end |
| 749 | return replies |
| 750 | end |
| 751 | |
| 752 | return vpp |