perezdecastro.org

Moikka! Here's Adrián. I work for Igalia.

ljndpi 0.0.3 released with nDPI 1.8 support

Although I have been away from Snabb-related work for a while, the fact that nDPI 1.8 was released didn’t went unnoticed. This library is an important building block for SnabbWall, the Layer-7 firewall which I am developing at Igalia with sponsorship from the NLnet Foundation (thanks!).

 

SnabbWall uses the nDPI library via the ljndpi binding, about which I wrote back in January. As any other component which uses a native library, ljndpi is a consumer of the API and ABI exported by it, so it needs to be updated to accomodate to changes introduced in new releases of nDPI.

Fortuntately most of the API (and ABI) from nDPI 1.7 remains unchanged in version 1.8, making it possible to maintain the interface exposed to the Lua world. This is very good news, and it means that any program using ljndpi does not need to be modified to work with nDPI 1.8: just update ljndpi to version 0.0.3 and you are ready to go.

Nitty-Gritty Details

In order to support versions 1.7 and 1.8 of the nDPI library, ndpi_revision() is used to obtain the version string of the loaded library, and parsed into a (major, minor, patch) version triplet. The ndpi.c module uses this version information to determine which symbols to make known to the FFI extension via ffi.cdef:

if lib_version.minor == 7 then
  -- Declare functions available only in nDPI 1.7
  ffi.cdef [[
    ...
  ]]
elseif lib_version.minor == 8 then
  -- Ditto, for version 1.8
  ffi.cdef [[
    ...
  ]]
end

The high-level ndpi.wrap module does the same, ensuring that only functions available in the version of the nDPI library currently loaded are used. In some cases, parameters passed to functions with the same name in nDPI may differ, and ndpi.wrap shuffles them around as needed in order to avoid changes in the interface exposed to Lua:

if lib_version.major == 7 then
   -- ...
else
   -- ...

   -- In nDPI 1.8 the second parameter (uint8_t proto) has been dropped.
   detection_module.find_port_based_protocol = function (dm, dummy, ...)
      local proto = lib.ndpi_find_port_based_protocol(dm, ...)
      return proto.master_protocol, proto.protocol
   end
end

Last but not least, the numeric values which identify each protocol supported by nDPI have changed as well, due to the additional protocols supported by the new version of the library. The existing ndpi.protocol_ids module was renamed to ndpi.protocol_ids_1_7, and then a new ndpi.protocol_ids_1_8 generated using the update-protocol-ids tool. Finally, the ndpi.wrap module was updated to load the module which corresponds to the version of the nDPI library being used:

-- Return the module table.
return {
  protocol = require("ndpi.protocol_ids_"
            .. lib_version.major .. "_"
            .. lib_version.minor);
  -- ...
}

Easier Installation

As a bonus, it is now possible to install ljndpi using the LuaRocks package manager. LuaRocks provides similar functionality to NodeJS’ npm or Python’s pip. If your LuaJIT installation includes LuaRocks, getting the latest stable release of ljndpi is easier than ever:

luarocks install ljndpi

It is also possible to install a development snapshot directly. The following will fetch the source from the Git repository and install it using LuaRocks — though it is strongly recommended to use the stable releases:

luarocks install --server=https://luarocks.org/dev ljndpi

Hopefully, this will make ljndpi easier to discover and used by projects other than SnabbWall. Who knows, it may even make it easier for distributions to package it! (Hint: LuaRocks supports specifying a custom tree.)

One More Thing…

He said it 31 times on stage (look it up!)

My previous post ended up in a cliffhanger which promised another post about SnabbWall. Supporting nDPI 1.8 in ljndpi seemed important enough to write about it, but rest assured that the post on SnabbWall is next in my “polish and publish” queue.