Skip to content

Instantly share code, notes, and snippets.

@lukego
Last active February 21, 2021 14:39
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save lukego/1626e7d31b54de803a48 to your computer and use it in GitHub Desktop.
Save lukego/1626e7d31b54de803a48 to your computer and use it in GitHub Desktop.
Example using snsh (snabb shell) to feed packets to a C program

This is a simple example of a low-level script that uses the Snabb Switch 82599 10G ethernet device driver directly to feed packets into a C function provided as a shared library. Written for a discussion with Pavel Odintsov.

Here is now to run it:

  1. Compile snabbswitch to get snabb executable.
  2. Compile a C callback function to a shared library e.g. capturecallback.c below.
  3. Run: sudo ./snabb snsh capture2c <pciaddress> <filename.so>

The output should look something like this:

sudo ./snabb snsh capture2c.lua 0000:01:00.0 capturecallback.so
Loading shared object: program/capture2c/capturecallback.so
Initializing NIC: 0000:01:00.0
Processing traffic...
Got packet with 64 bytes.
Got packet with 64 bytes.
Got packet with 64 bytes.
Got packet with 64 bytes.
Got packet with 64 bytes.

The program takes advantage of Lua letting Snabb Switch be scripted after it has been compiled and of the LuaJIT FFI that makes it easy for Snabb Switch to call C.

The 10G device driver is itself implemented purely in Lua by the module intel10g.lua.

if #main.parameters ~= 2 then
print([[Usage: capture2c <pciaddress> <my.so>
Capture network traffic from an Intel 82599 NIC at <pciaddress> and
pass each packet to a callback function defined in the shared library
<my.so>.
The callback function signature is:
void packet(char *data, int length);]])
os.exit(1)
end
local pciaddr, sofile = unpack(main.parameters)
-- Load shared object
print("Loading shared object: "..sofile)
local ffi = require("ffi")
local so = ffi.load(sofile)
ffi.cdef("void packet(char *data, int length);")
-- Initialize a device driver
print("Initializing NIC: "..pciaddr)
local intel10g = require("apps.intel.intel10g")
-- Maximum buffers to avoid packet drops
intel10g.num_descriptors = 32*1024
local nic = intel10g.new_sf({pciaddr=pciaddr})
nic:open()
-- Process traffic in infinite loop
print("Processing traffic...")
while true do
-- Fill up the NIC with receive buffers
while nic:can_add_receive_buffer() do
nic:add_receive_buffer(packet.allocate())
end
-- Process packets via callback.
while nic:can_receive() do
local p = nic:receive()
so.packet(p.data, p.length)
packet.free(p)
end
-- Update hardware ring
nic:sync_receive()
end
// compile with: gcc -shared -o capturecallback.so -fPIC capturecallback.c
#include <stdio.h>
void packet(char *data, int length) {
printf("Got packet with %d bytes.\n", length);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment