Lunatik

tcModule

Linux Traffic Control (TC) integration.

This library allows Lua scripts to interact with the kernel’s TC subsystem. It enables TC/eBPF programs to call Lua functions for packet processing, traffic shaping, filtering, and policy enforcement, providing a flexible way to implement custom networking logic in Lua at the ingress and egress layers of network stack.

The primary mechanism involves a TC program calling the bpf_luatc_run kfunc, which in turn invokes a Lua callback function previously registered using tc.attach().

The module registers its kfunc through BTF: it needs a kernel built with CONFIG_DEBUG_INFO_BTF and a build made after make btf_install. Without the module BTF it loads with its kfunc unregistered, logging missing module BTF, and an eBPF program that calls the kfunc fails to load. The eBPF program is loaded apart, with bpftool, as a SCHED_CLS (classifier) program that declares bpf_luatc_run as a __ksym, as examples/sniclassify does.

Class tc_ctx

tc_ctx:argument () Returns the argument data buffer passed from eBPF.
tc_ctx:skb () Returns the packet object for the current TC context.

tc

attach (callback) Registers a Lua callback function to be invoked by a TC/eBPF program.
detach () Unregisters the Lua callback function associated with the current Lunatik runtime.

Class tc_ctx in detail

TC callback context, valid only while the callback runs. It is handed to the callback registered with tc.attach; its methods raise once the callback returns.
tc_ctx:argument ()
Returns the argument data buffer passed from eBPF.

Returns

    data argument buffer
tc_ctx:skb ()
Returns the packet object for the current TC context.

Returns

    skb

tc in detail

attach (callback)
Registers a Lua callback function to be invoked by a TC/eBPF program. When a TC program calls the bpf_luatc_run kfunc, Lunatik will execute the registered Lua callback associated with the current Lunatik runtime. The runtime must be softirq, lunatik run -c softirq <script>.

The bpf_luatc_run kfunc is called from an eBPF program with the following signature: int bpf_luatc_run(char key, size_t key__sz, struct __sk_buff sk_buff, void *arg, size_t arg__sz)

  • key: A string identifying the Lunatik runtime: the script path under /lib/modules/lua without its .lua extension, as given to lunatik run (e.g. “examples/sniclassify/sni”). This key is used to look up the runtime in Lunatik’s internal table of active runtimes.
  • key_sz: Length of the key string (including the null terminator).
  • sk_buff: The TC metadata context (struct __sk_buff *).
  • arg: A pointer to arbitrary data passed from eBPF to Lua.
  • arg_sz: The size of the arg data.

In the callback, the skb methods that would linearize a packet raise on a non-linear one instead, such as a locally sent TCP segment or a GRO-merged one: the verifier keeps the program’s packet pointers across a kfunc, and linearizing can move the packet out from under them. A program whose callback reads the whole packet calls bpf_skb_pull_data(skb, skb->len) before bpf_luatc_run, after which the verifier has it reload its packet pointers.

Parameters

  • callback function Lua function to call. It receives one argument:

    ctx: A tc_ctx context object used to inspect the packet.

    It returns the verdict, a value of linux.tc.action, which bpf_luatc_run returns to the eBPF program. When it returns nothing or nil, or raises, bpf_luatc_run returns -1 and the verdict is left to the eBPF program; a value that is not a TC action is logged as invalid action and answered the same way.

Returns

    nil

Raises

runtime context mismatch outside a softirq runtime; not allowed while the runtime closes from a finalizer that runs at its close; or if internal setup fails.

See also

Usage

-- Lua script my_tc_handler.lua, run with lunatik run -c softirq my_tc_handler
local tc = require("tc")
local action = require("linux.tc").action

local function my_traffic_shaper(ctx)
  local skb = ctx:skb()
  print("Packet received, size:", #skb)
  return action.OK
end
tc.attach(my_traffic_shaper)

-- In eBPF C code, to call the above Lua function:
-- char rt_key[] = "my_tc_handler"; // the script name without .lua
-- int verdict = bpf_luatc_run(rt_key, sizeof(rt_key), skb, NULL, 0);
detach ()
Unregisters the Lua callback function associated with the current Lunatik runtime. After calling this, bpf_luatc_run calls targeting this runtime invoke no Lua function: they log no callback attached, return -1 and leave the verdict to the eBPF program.

Returns

    nil

Usage

tc.detach()