AI prompts
base on Lunatik is a framework for scripting the Linux kernel with Lua. # Lunatik
Lunatik is a framework for scripting the Linux kernel with [Lua](https://www.lua.org/).
It is composed by the Lua interpreter modified to run in the kernel;
a [device driver](driver.lua) (written in Lua =)) and a [command line tool](bin/lunatik)
to load and run scripts and manage runtime environments from the user space;
a [C API](#lunatik-c-api) to load and run scripts and manage runtime environments from the kernel;
and [Lua APIs](#lunatik-lua-apis) for binding kernel facilities to Lua scripts.
Here is an example of a character device driver written in Lua using Lunatik
to generate random ASCII printable characters:
```Lua
-- /lib/modules/lua/passwd.lua
--
-- implements /dev/passwd for generate passwords
-- usage: $ sudo lunatik run passwd
-- $ head -c <width> /dev/passwd
local device = require("device")
local linux = require("linux")
local function nop() end -- do nothing
local s = linux.stat
local driver = {name = "passwd", open = nop, release = nop, mode = s.IRUGO}
function driver:read() -- read(2) callback
-- generate random ASCII printable characters
return string.char(linux.random(32, 126))
end
-- creates a new character device
device.new(driver)
```
## Setup
Install dependencies (here for Debian/Ubuntu, to be adapted to one's distribution):
```sh
sudo apt install git build-essential lua5.4 dwarves clang llvm libelf-dev linux-headers-$(uname -r) linux-tools-common linux-tools-$(uname -r) pkg-config libpcap-dev m4
```
Compile and install `lunatik`:
```sh
LUNATIK_DIR=~/lunatik # to be adapted
mkdir "${LUNATIK_DIR}" ; cd "${LUNATIK_DIR}"
git clone --depth 1 --recurse-submodules https://github.com/luainkernel/lunatik.git
cd lunatik
make
sudo make install
```
Once done, the `debian_kernel_postinst_lunatik.sh` script from tools/ may be copied into
`/etc/kernel/postinst.d/`: this ensures `lunatik` (and also the `xdp` needed libs) will get
compiled on kernel upgrade.
## Usage
```
sudo lunatik # execute Lunatik REPL
Lunatik 3.5 Copyright (C) 2023-2024 ring-0 Ltda.
> return 42 -- execute this line in the kernel
42
```
### lunatik
```Shell
usage: lunatik [load|unload|reload|status|list] [run|spawn|stop <script>]
```
* `load`: load Lunatik kernel modules
* `unload`: unload Lunatik kernel modules
* `reload`: reload Lunatik kernel modules
* `status`: show which Lunatik kernel modules are currently loaded
* `list`: show which runtime environments are currently running
* `run`: create a new runtime environment to run the script `/lib/modules/lua/<script>.lua`
* `spawn`: create a new runtime environment and spawn a thread to run the script `/lib/modules/lua/<script>.lua`
* `stop`: stop the runtime environment created to run the script `<script>`
* `default`: start a _REPL (Read–Eval–Print Loop)_
## Lua Version
Lunatik 3.4 is based on
[Lua 5.4 adapted](https://github.com/luainkernel/lua)
to run in the kernel.
### Floating-point numbers
Lunatik **does not** support floating-point arithmetic,
thus it **does not** support `__div` nor `__pow`
[metamethods](https://www.lua.org/manual/5.4/manual.html#2.4)
and the type _number_ has only the subtype _integer_.
### Lua API
Lunatik **does not** support both [io](https://www.lua.org/manual/5.4/manual.html#6.8) and
[os](https://www.lua.org/manual/5.4/manual.html#6.9) libraries,
and the given identifiers from the following libraries:
* [debug.debug](https://www.lua.org/manual/5.4/manual.html#pdf-debug.debug),
[math.acos](https://www.lua.org/manual/5.4/manual.html#pdf-math.acos),
[math.asin](https://www.lua.org/manual/5.4/manual.html#pdf-math.asin),
[math.atan](https://www.lua.org/manual/5.4/manual.html#pdf-math.atan),
[math.ceil](https://www.lua.org/manual/5.4/manual.html#pdf-math.ceil),
[math.cos](https://www.lua.org/manual/5.4/manual.html#pdf-math.cos),
[math.deg](https://www.lua.org/manual/5.4/manual.html#pdf-math.deg),
[math.exp](https://www.lua.org/manual/5.4/manual.html#pdf-math.exp),
[math.floor](https://www.lua.org/manual/5.4/manual.html#pdf-math.floor),
[math.fmod](https://www.lua.org/manual/5.4/manual.html#pdf-math.fmod),
[math.huge](https://www.lua.org/manual/5.4/manual.html#pdf-math.huge).
[math.log](https://www.lua.org/manual/5.4/manual.html#pdf-math.log),
[math.modf](https://www.lua.org/manual/5.4/manual.html#pdf-math.modf),
[math.pi](https://www.lua.org/manual/5.4/manual.html#pdf-math.pi),
[math.rad](https://www.lua.org/manual/5.4/manual.html#pdf-math.rad),
[math.random](https://www.lua.org/manual/5.4/manual.html#pdf-math.random),
[math.randomseed](https://www.lua.org/manual/5.4/manual.html#pdf-math.randomseed),
[math.sin](https://www.lua.org/manual/5.4/manual.html#pdf-math.sin),
[math.sqrt](https://www.lua.org/manual/5.4/manual.html#pdf-math.sqrt),
[math.tan](https://www.lua.org/manual/5.4/manual.html#pdf-math.tan),
[math.type](https://www.lua.org/manual/5.4/manual.html#pdf-math.type),
[package.cpath](https://www.lua.org/manual/5.4/manual.html#pdf-package.cpath).
Lunatik **modifies** the following identifiers:
* [\_VERSION](https://www.lua.org/manual/5.4/manual.html#pdf-_VERSION): is defined as `"Lua 5.4-kernel"`.
* [collectgarbage("count")](https://www.lua.org/manual/5.4/manual.html#pdf-collectgarbage): returns the total memory in use by Lua in **bytes**, instead of _Kbytes_.
* [package.path](https://www.lua.org/manual/5.4/manual.html#pdf-package.path): is defined as `"/lib/modules/lua/?.lua;/lib/modules/lua/?/init.lua"`.
* [require](https://www.lua.org/manual/5.4/manual.html#pdf-require): only supports built-in or already linked C modules, that is, Lunatik **cannot** load kernel modules dynamically.
### C API
Lunatik **does not** support
[luaL\_Stream](https://www.lua.org/manual/5.4/manual.html#luaL_Stream),
[luaL\_execresult](https://www.lua.org/manual/5.4/manual.html#luaL_execresult),
[luaL\_fileresult](https://www.lua.org/manual/5.4/manual.html#luaL_fileresult),
[luaopen\_io](https://www.lua.org/manual/5.4/manual.html#pdf-luaopen_io) and
[luaopen\_os](https://www.lua.org/manual/5.4/manual.html#pdf-luaopen_os).
Lunatik **modifies** [luaL\_openlibs](https://www.lua.org/manual/5.4/manual.html#luaL_openlibs) to remove [luaopen\_io](https://www.lua.org/manual/5.4/manual.html#pdf-luaopen_io) and [luaopen\_os](https://www.lua.org/manual/5.4/manual.html#pdf-luaopen_os).
## Lunatik C API
```C
#include <lunatik.h>
```
#### lunatik\_runtime
```C
int lunatik_runtime(lunatik_object_t **pruntime, const char *script, bool sleep);
```
_lunatik\_runtime()_ creates a new `runtime` environment then loads and runs the script
`/lib/modules/lua/<script>.lua` as the entry point for this environment.
It _must_ only be called from _process context_.
The `runtime` environment is a Lunatik object that holds
a [Lua state](https://www.lua.org/manual/5.4/manual.html#lua_State).
Lunatik objects are special
Lua [userdata](https://www.lua.org/manual/5.4/manual.html#2.1)
which also hold
a [lock type](https://docs.kernel.org/locking/locktypes.html) and
a [reference counter](https://www.kernel.org/doc/Documentation/kref.txt).
If `sleep` is _true_, _lunatik\_runtime()_ will use a
[mutex](https://docs.kernel.org/locking/mutex-design.html)
for locking the `runtime` environment and the
[GFP\_KERNEL](https://www.kernel.org/doc/html/latest/core-api/memory-allocation.html)
flag for allocating new memory later on on
[lunatik\_run()](https://github.com/luainkernel/lunatik#lunatik_run) calls.
Otherwise, it will use a [spinlock](https://docs.kernel.org/locking/locktypes.html#raw-spinlock-t-and-spinlock-t) and [GFP\_ATOMIC](https://www.kernel.org/doc/html/latest/core-api/memory-allocation.html).
_lunatik\_runtime()_ opens the Lua standard libraries
[present on Lunatik](https://github.com/luainkernel/lunatik#c-api).
If successful, _lunatik\_runtime()_ sets the address pointed by `pruntime` and
[Lua's extra space](https://www.lua.org/manual/5.4/manual.html#lua_getextraspace)
with a pointer for the new created `runtime` environment,
sets the _reference counter_ to `1` and then returns `0`.
Otherwise, it returns `-ENOMEM`, if insufficient memory is available;
or `-EINVAL`, if it fails to load or run the `script`.
##### Example
```Lua
-- /lib/modules/lua/mydevice.lua
function myread(len, off)
return "42"
end
```
```C
static lunatik_object_t *runtime;
static int __init mydevice_init(void)
{
return lunatik_runtime(&runtime, "mydevice", true);
}
```
#### lunatik\_stop
```C
int lunatik_stop(lunatik_object_t *runtime);
```
_lunatik\_stop()_
[closes](https://www.lua.org/manual/5.4/manual.html#lua_close)
the
[Lua state](https://www.lua.org/manual/5.4/manual.html#lua_State)
created for this `runtime` environment and decrements the
[reference counter](https://www.kernel.org/doc/Documentation/kref.txt).
Once the reference counter is decremented to zero, the
[lock type](https://docs.kernel.org/locking/locktypes.html)
and the memory allocated for the `runtime` environment are released.
If the `runtime` environment has been released, it returns `1`;
otherwise, it returns `0`.
#### lunatik\_run
```C
void lunatik_run(lunatik_object_t *runtime, <inttype> (*handler)(...), <inttype> &ret, ...);
```
_lunatik\_run()_ locks the `runtime` environment and calls the `handler`
passing the associated Lua state as the first argument followed by the variadic arguments.
If the Lua state has been closed, `ret` is set with `-ENXIO`;
otherwise, `ret` is set with the result of `handler(L, ...)` call.
Then, it restores the Lua stack and unlocks the `runtime` environment.
It is defined as a macro.
##### Example
```C
static int l_read(lua_State *L, char *buf, size_t len, loff_t *off)
{
size_t llen;
const char *lbuf;
lua_getglobal(L, "myread");
lua_pushinteger(L, len);
lua_pushinteger(L, *off);
if (lua_pcall(L, 2, 2, 0) != LUA_OK) { /* calls myread(len, off) */
pr_err("%s\n", lua_tostring(L, -1));
return -ECANCELED;
}
lbuf = lua_tolstring(L, -2, &llen);
llen = min(len, llen);
if (copy_to_user(buf, lbuf, llen) != 0)
return -EFAULT;
*off = (loff_t)luaL_optinteger(L, -1, *off + llen);
return (ssize_t)llen;
}
static ssize_t mydevice_read(struct file *f, char *buf, size_t len, loff_t *off)
{
ssize_t ret;
lunatik_object_t *runtime = (lunatik_object_t *)f->private_data;
lunatik_run(runtime, l_read, ret, buf, len, off);
return ret;
}
```
#### lunatik\_getobject
```C
void lunatik_getobject(lunatik_object_t *object);
```
_lunatik\_getobject()_ increments the
[reference counter](https://www.kernel.org/doc/Documentation/kref.txt)
of this `object` (e.g., `runtime` environment).
#### lunatik\_put
```C
int lunatik_putobject(lunatik_object_t *object);
```
_lunatik\_putobject()_ decrements the
[reference counter](https://www.kernel.org/doc/Documentation/kref.txt)
of this `object` (e.g., `runtime` environment).
If the `object` has been released, it returns `1`;
otherwise, it returns `0`.
#### lunatik\_toruntime
```C
lunatik_object_t *lunatik_toruntime(lua_State *L);
```
_lunatik\_toruntime()_ returns the `runtime` environment referenced by the `L`'s
[extra space](https://www.lua.org/manual/5.4/manual.html#lua_getextraspace).
## Lunatik Lua APIs
### lunatik
The `lunatik` library provides support to load and run scripts and manage runtime environments from Lua.
#### `lunatik.runtime(script [, sleep])`
_lunatik.runtime()_ creates a new
[runtime environment](https://github.com/luainkernel/lunatik#lunatik_runtime)
then loads and runs the script
`/lib/modules/lua/<script>.lua` as the entry point for this environment.
It returns a Lunatik object representing the `runtime` environment.
If `sleep` is _true_ or omitted, it will use a [mutex](https://docs.kernel.org/locking/mutex-design.html)
and
[GFP\_KERNEL](https://www.kernel.org/doc/html/latest/core-api/memory-allocation.html);
otherwise, it will use a [spinlock](https://docs.kernel.org/locking/locktypes.html#raw-spinlock-t-and-spinlock-t) and [GFP\_ATOMIC](https://www.kernel.org/doc/html/latest/core-api/memory-allocation.html).
_lunatik.runtime()_ opens the Lua standard libraries
[present on Lunatik](https://github.com/luainkernel/lunatik#c-api).
#### `runtime:stop()`
_runtime:stop()_
[stops](https://github.com/luainkernel/lunatik#lunatik_stop)
the `runtime` environment and clear its reference from the runtime object.
#### `runtime:resume([obj1, ...])`
_runtime:resume()_
resumes the execution of a `runtime`.
The values `obj1, ...` are passed as the arguments to the function returned on the `runtime` creation.
If the `runtime` has yielded, `resume()` restarts it; the values `obj1, ...` are passed as the results from the yield.
### device
The `device` library provides support for writting
[character device drivers](https://static.lwn.net/images/pdf/LDD3/ch03.pdf)
in Lua.
#### `device.new(driver)`
_device.new()_ returns a new `device` object
and installs its `driver` in the system.
The `driver` **must** be defined as a table containing the following field:
* `name`: string defining the device name; it is used for creating the device file (e.g., `/dev/<name>`).
The `driver` table might optionally contain the following fields:
* `read`: callback function to handle the
[read operation](https://docs.kernel.org/filesystems/vfs.html#id2)
on the device file.
It receives the `driver` table as the first argument
followed by two integers,
the `length` to be read and the file `offset`.
It should return a string and, optionally, the `updated offset`.
If the length of the returned string is greater than the requested `length`,
the string will be corrected to that `length`.
If the `updated offset` is not returned, the `offset` will be updated with `offset + length`.
* `write`: callback function to handle the
[write operation](https://docs.kernel.org/filesystems/vfs.html#id2)
on the device file.
It receives the `driver` table as the first argument
followed by the string to be written and
an integer as the file `offset`.
It might return optionally the written `length` followed by the `updated offset`.
If the returned length is greater than the requested `length`,
the returned length will be corrected.
If the `updated offset` is not returned, the `offset` will be updated with `offset + length`.
* `open`: callback function to handle the
[open operation](https://docs.kernel.org/filesystems/vfs.html#id2)
on the device file.
It receives the `driver` table and it is expected to return nothing.
* `release`: callback function to handle the
[release operation](https://docs.kernel.org/filesystems/vfs.html#id2)
on the device file.
It receives the `driver` table and it is expected to return nothing.
* `mode`: an integer specifying the device
[file mode](https://github.com/luainkernel/lunatik#linuxstat).
If an operation callback is not defined, the `device` returns `-ENXIO` to VFS on its access.
#### `device.stop(dev)`, `dev:stop()`
_device.stop()_ removes a device `driver` specified by the `dev` object from the system.
### linux
The `linux` library provides support for some Linux kernel facilities.
#### `linux.random([m [, n]])`
_linux.random()_ mimics the behavior of
[math.random](https://www.lua.org/manual/5.4/manual.html#pdf-math.random),
but binding _<linux/random.h>_'s
[get\_random\_u32()](https://elixir.bootlin.com/linux/latest/source/include/linux/random.h#L42)
and
[get\_random\_u64()](https://elixir.bootlin.com/linux/latest/source/include/linux/random.h#L43)
APIs.
When called without arguments,
produces an integer with all bits (pseudo)random.
When called with two integers `m` and `n`,
_linux.random()_ returns a pseudo-random integer with uniform distribution in the range `[m, n]`.
The call `math.random(n)`, for a positive `n`, is equivalent to `math.random(1, n)`.
#### `linux.stat`
_linux.stat_ is a table that exports
[\<linux/stat.h\>](https://elixir.bootlin.com/linux/latest/source/include/linux/stat.h)
integer flags to Lua.
* `"IRWXUGO"`: permission to _read_, _write_ and _execute_ for _user_, _group_ and _other_.
* `"IRUGO"`: permission only to _read_ for _user_, _group_ and _other_.
* `"IWUGO"`: permission only to _write_ for _user_, _group_ and _other_.
* `"IXUGO"`: permission only to _execute_ for _user_, _group_ and _other_.
#### `linux.schedule([timeout [, state]])`
_linux.schedule()_ sets the current task `state` and makes the it sleep until `timeout` milliseconds have elapsed.
If `timeout` is omitted, it uses `MAX_SCHEDULE_TIMEOUT`.
If `state` is omitted, it uses `task.INTERRUPTIBLE`.
#### `linux.task`
_linux.task_ is a table that exports
[task state](https://elixir.bootlin.com/linux/latest/source/include/linux/sched.h#L7v3)
flags to Lua.
* `"RUNNING"`: task is executing on a CPU or waiting to be executed.
* `"INTERRUPTIBLE"`: task is waiting for a signal or a resource (sleeping).
* `"UNINTERRUPTIBLE"`: behaves like "INTERRUPTIBLE" with the exception that signal will not wake up the task.
* `"KILLABLE"`: behaves like "UNINTERRUPTIBLE" with the exception that fatal signals will wake up the task.
* `"IDLE"`: behaves like "UNINTERRUPTIBLE" with the exception that it avoids the loadavg accounting.
#### `linux.time()`
_linux.time()_ returns the current time in nanoseconds since epoch.
#### `linux.errno`
_linux.errno_ is a table that exports
[\<uapi/asm-generic/errno-base.h\>](https://elixir.bootlin.com/linux/latest/source/include/uapi/asm-generic/errno-base.h)
flags to Lua.
* `"PERM"`: Operation not permitted.
* `"NOENT"`: No such file or directory.
* `"SRCH"`: No such process.
* `"INTR"`: Interrupted system call.
* `"IO"`: I/O error.
* `"NXIO"`:No such device or address.
* `"2BIG"`:, Argument list too long.
* `"NOEXEC"`: Exec format error.
* `"BADF"`: Bad file number.
* `"CHILD"`: No child processes.
* `"AGAIN"`: Try again.
* `"NOMEM"`: Out of memory.
* `"ACCES"`: Permission denied.
* `"FAULT"`: Bad address.
* `"NOTBLK"`: Block device required.
* `"BUSY"`: Device or resource busy.
* `"EXIST"`: File exists.
* `"XDEV"`: Cross-device link.
* `"NODEV"`: No such device.
* `"NOTDIR"`: Not a directory.
* `"ISDIR"`: Is a directory.
* `"INVAL"`: Invalid argument.
* `"NFILE"`: File table overflow.
* `"MFILE"`: Too many open files.
* `"NOTTY"`: Not a typewriter.
* `"TXTBSY"`: Text file busy.
* `"FBIG"`: File too large.
* `"NOSPC"`: No space left on device.
* `"SPIPE"`: Illegal seek.
* `"ROFS"`: Read-only file system.
* `"MLINK"`: Too many links.
* `"PIPE"`: Broken pipe.
* `"DOM"`: Math argument out of domain of func.
* `"RANGE"`: Math result not representable.
#### `linux.hton16(num)`
_linux.hton16()_ converts the host byte order to network byte order for a 16-bit integer.
#### `linux.hton32(num)`
_linux.hton32()_ converts the host byte order to network byte order for a 32-bit integer.
#### `linux.hton64(num)`
_linux.hton64()_ converts the host byte order to network byte order for a 64-bit integer.
#### `linux.ntoh16(num)`
_linux.ntoh16()_ converts the network byte order to host byte order for a 16-bit integer.
#### `linux.ntoh32(num)`
_linux.ntoh32()_ converts the network byte order to host byte order for a 32-bit integer.
#### `linux.ntoh64(num)`
_linux.ntoh64()_ converts the network byte order to host byte order for a 64-bit integer.
#### `linux.htobe16(num)`
_linux.htobe16()_ converts the host byte order to big-endian byte order for a 16-bit integer.
#### `linux.htobe32(num)`
_linux.htobe32()_ converts the host byte order to big-endian byte order for a 32-bit integer.
#### `linux.htobe64(num)`
_linux.htobe64()_ converts the host byte order to big-endian byte order for a 64-bit integer.
#### `linux.be16toh(num)`
_linux.be16toh()_ converts the big-endian byte order to host byte order for a 16-bit integer.
#### `linux.be32toh(num)`
_linux.be32toh()_ converts the big-endian byte order to host byte order for a 32-bit integer.
#### `linux.be64toh(num)`
_linux.be64toh()_ converts the big-endian byte order to host byte order for a 64-bit integer.
#### `linux.htole16(num)`
_linux.htole16()_ converts the host byte order to little-endian byte order for a 16-bit integer.
#### `linux.htole32(num)`
_linux.htole32()_ converts the host byte order to little-endian byte order for a 32-bit integer.
#### `linux.htole64(num)`
_linux.htole64()_ converts the host byte order to little-endian byte order for a 64-bit integer.
#### `linux.le16toh(num)`
_linux.le16toh()_ converts the little-endian byte order to host byte order for a 16-bit integer.
#### `linux.le32toh(num)`
_linux.le32toh()_ converts the little-endian byte order to host byte order for a 32-bit integer.
#### `linux.le64toh(num)`
_linux.le64toh()_ converts the little-endian byte order to host byte order for a 64-bit integer.
### notifier
The `notifier` library provides support for the kernel
[notifier chains](https://elixir.bootlin.com/linux/latest/source/include/linux/notifier.h).
#### `notifier.keyboard(callback)`
_notifier.keyboard()_ returns a new keyboard `notifier` object and installs it in the system.
The `callback` function is called whenever a console keyboard event happens
(e.g., a key has been pressed or released).
This `callback` receives the following arguments:
* `event`: the available _events_ are defined by the
[notifier.kbd](https://github.com/luainkernel/lunatik#notifierkbd) table.
* `down`: `true`, if the key is pressed; `false`, if it is released.
* `shift`: `true`, if the shift key is held; `false`, otherwise.
* `key`: _keycode_ or _keysym_ depending on `event`.
The `callback` function might return the values defined by the
[notifier.notify](https://github.com/luainkernel/lunatik#notifiernotify) table.
#### `notifier.kbd`
_notifier.kbd_ is a table that exports
[KBD](https://elixir.bootlin.com/linux/latest/source/include/linux/notifier.h#L229)
flags to Lua.
* `"KEYCODE"`: keyboard _keycode_, called before any other.
* `"UNBOUND_KEYCODE"`: keyboard _keycode_ which is not bound to any other.
* `"UNICODE"`: keyboard unicode.
* `"KEYSYM"`: keyboard _keysym_.
* `"POST_KEYSYM"`: called after keyboard _keysym_ interpretation.
#### `notifier.netdevice(callback)`
_notifier.netdevice()_ returns a new netdevice `notifier` object and installs it in the system.
The `callback` function is called whenever a console netdevice event happens
(e.g., a network interface has been connected or disconnected).
This `callback` receives the following arguments:
* `event`: the available _events_ are defined by the
[notifier.netdev](https://github.com/luainkernel/lunatik#notifiernetdev) table.
* `name`: the device name.
The `callback` function might return the values defined by the
[notifier.notify](https://github.com/luainkernel/lunatik#notifiernotify) table.
#### `notifier.netdev`
_notifier.netdev_ is a table that exports
[NETDEV](https://elixir.bootlin.com/linux/v6.3/source/include/linux/netdevice.h#L2812)
flags to Lua.
#### `notifier.notify`
_notifier.notify_ is a table that exports
[NOTIFY](https://elixir.bootlin.com/linux/latest/source/include/linux/notifier.h#L183)
flags to Lua.
* `"DONE"`: don't care.
* `"OK"`: suits me.
* `"BAD"`: bad/veto action.
* `"STOP"`: clean way to return from the notifier and stop further calls.
#### `notfr:delete()`
_notfr:delete()_ removes a `notifier` specified by the `notfr` object from the system.
### socket
The `socket` library provides support for the kernel
[networking handling](https://elixir.bootlin.com/linux/latest/source/include/linux/net.h).
This library was inspired by
[Chengzhi Tan](https://github.com/tcz717)'s
[GSoC project](https://summerofcode.withgoogle.com/archive/2018/projects/5993341447569408).
#### `socket.new(family, type, protocol)`
_socket.new()_ creates a new `socket` object.
This function receives the following arguments:
* `family`: the available _address families_ are defined by the
[socket.af](https://github.com/luainkernel/lunatik#socketaf) table.
* `sock`: the available _types_ are present on the
[socket.sock](https://github.com/luainkernel/lunatik#socketsock) table.
* `protocol`: the available _protocols_ are defined by the
[socket.ipproto](https://github.com/luainkernel/lunatik#socketipproto) table.
#### `socket.af`
_socket.af_ is a table that exports
[address families (AF)](https://elixir.bootlin.com/linux/latest/source/include/linux/socket.h#L187)
to Lua.
* `"UNSPEC"`: Unspecified.
* `"UNIX"`: Unix domain sockets.
* `"LOCAL"`: POSIX name for AF\_UNIX.
* `"INET"`: Internet IP Protocol.
* `"AX25"`: Amateur Radio AX.25.
* `"IPX"`: Novell IPX.
* `"APPLETALK"`: AppleTalk DDP.
* `"NETROM"`: Amateur Radio NET/ROM.
* `"BRIDGE"`: Multiprotocol bridge.
* `"ATMPVC"`: ATM PVCs.
* `"X25"`: Reserved for X.25 project.
* `"INET6"`: IP version 6.
* `"ROSE"`: Amateur Radio X.25 PLP.
* `"DEC"`: Reserved for DECnet project.
* `"NETBEUI"`: Reserved for 802.2LLC project.
* `"SECURITY"`: Security callback pseudo AF.
* `"KEY"`: PF\_KEY key management API.
* `"NETLINK"`: Netlink.
* `"ROUTE"`: Alias to emulate 4.4BSD.
* `"PACKET"`: Packet family.
* `"ASH"`: Ash.
* `"ECONET"`: Acorn Econet.
* `"ATMSVC"`: ATM SVCs.
* `"RDS"`: RDS sockets.
* `"SNA"`: Linux SNA Project (nutters!).
* `"IRDA"`: IRDA sockets.
* `"PPPOX"`: PPPoX sockets.
* `"WANPIPE"`: Wanpipe API Sockets.
* `"LLC"`: Linux LLC.
* `"IB"`: Native InfiniBand address.
* `"MPLS"`: MPLS.
* `"CAN"`: Controller Area Network.
* `"TIPC"`: TIPC sockets.
* `"BLUETOOTH"`: Bluetooth sockets.
* `"IUCV"`: IUCV sockets.
* `"RXRPC"`: RxRPC sockets.
* `"ISDN"`: mISDN sockets.
* `"PHONET"`: Phonet sockets.
* `"IEEE802154"`: IEEE802154 sockets.
* `"CAIF"`: CAIF sockets.
* `"ALG"`: Algorithm sockets.
* `"NFC"`: NFC sockets.
* `"VSOCK"`: vSockets.
* `"KCM"`: Kernel Connection Multiplexor.
* `"QIPCRTR"`: Qualcomm IPC Router.
* `"SMC"`: reserve number for PF\_SMC protocol family that reuses AF\_INET address family.
* `"XDP"`: XDP sockets.
* `"MCTP"`: Management component transport protocol.
* `"MAX"`: Maximum.
#### `socket.sock`
_socket.sock_ is a table that exports socket
[types (SOCK)](https://elixir.bootlin.com/linux/latest/source/include/linux/net.h#L49):
* `"STREAM"`: stream (connection) socket.
* `"DGRAM"`: datagram (conn.less) socket.
* `"RAW"`: raw socket.
* `"RDM"`: reliably-delivered message.
* `"SEQPACKET"`: sequential packet socket.
* `"DCCP"`: Datagram Congestion Control Protocol socket.
* `"PACKET"`: linux specific way of getting packets at the dev level.
and [flags (SOCK)](https://elixir.bootlin.com/linux/latest/source/include/linux/net.h#L78):
* `"CLOEXEC"`: n/a.
* `"NONBLOCK"`: n/a.
#### `socket.ipproto`
_socket.ipproto_ is a table that exports
[IP protocols (IPPROTO)](https://elixir.bootlin.com/linux/latest/source/include/uapi/linux/in.h#L27)
to Lua.
* `"IP"`: Dummy protocol for TCP.
* `"ICMP"`: Internet Control Message Protocol.
* `"IGMP"`: Internet Group Management Protocol.
* `"IPIP"`: IPIP tunnels (older KA9Q tunnels use 94).
* `"TCP"`: Transmission Control Protocol.
* `"EGP"`: Exterior Gateway Protocol.
* `"PUP"`: PUP protocol.
* `"UDP"`: User Datagram Protocol.
* `"IDP"`: XNS IDP protocol.
* `"TP"`: SO Transport Protocol Class 4.
* `"DCCP"`: Datagram Congestion Control Protocol.
* `"IPV6"`: IPv6-in-IPv4 tunnelling.
* `"RSVP"`: RSVP Protocol.
* `"GRE"`: Cisco GRE tunnels (rfc 1701,1702).
* `"ESP"`: Encapsulation Security Payload protocol.
* `"AH"`: Authentication Header protocol.
* `"MTP"`: Multicast Transport Protocol.
* `"BEETPH"`: IP option pseudo header for BEET.
* `"ENCAP"`: Encapsulation Header.
* `"PIM"`: Protocol Independent Multicast.
* `"COMP"`: Compression Header Protocol.
* `"SCTP"`: Stream Control Transport Protocol.
* `"UDPLITE"`: UDP-Lite (RFC 3828).
* `"MPLS"`: MPLS in IP (RFC 4023).
* `"ETHERNET"`: Ethernet-within-IPv6 Encapsulation.
* `"RAW"`: Raw IP packets.
* `"MPTCP"`: Multipath TCP connection.
#### `sock:close()`
_sock:close()_ removes `sock` object from the system.
#### `sock:send(message, [addr [, port]])`
_sock:send()_ sends a string `message` through the socket `sock`.
If the `sock` address family is `af.INET`, then it expects the following arguments:
* `addr`: `integer` describing the destination IPv4 address.
* `port`: `integer` describing the destination IPv4 port.
Otherwise:
* `addr`: [packed string](https://www.lua.org/manual/5.4/manual.html#6.4.2) describing the destination address.
#### `sock:receive(length, [flags [, from]])`
_sock:receive()_ receives a string with up to `length` bytes through the socket `sock`.
The available _message flags_ are defined by the
[socket.msg](https://github.com/luainkernel/lunatik#socketmsg) table.
If `from` is `true`, it returns the received message followed by the peer's address.
Otherwise, it returns only the received message.
#### `socket.msg`
_socket.msg_ is a table that exports
[message flags](https://elixir.bootlin.com/linux/latest/source/include/linux/socket.h#L298)
to Lua.
* `"OOB"`: n/a.
* `"PEEK"`: n/a.
* `"DONTROUTE"`: n/a.
* `"TRYHARD"`: Synonym for `"DONTROUTE"` for DECnet.
* `"CTRUNC"`: n/a.
* `"PROBE"`: Do not send. Only probe path f.e. for MTU.
* `"TRUNC"`: n/a.
* `"DONTWAIT"`: Nonblocking io.
* `"EOR"`: End of record.
* `"WAITALL"`: Wait for a full request.
* `"FIN"`: n/a.
* `"SYN"`: n/a.
* `"CONFIRM"`: Confirm path validity.
* `"RST"`: n/a.
* `"ERRQUEUE"`: Fetch message from error queue.
* `"NOSIGNAL"`: Do not generate SIGPIPE.
* `"MORE"`: Sender will send more.
* `"WAITFORONE"`: recvmmsg(): block until 1+ packets avail.
* `"SENDPAGE_NOPOLICY"`: sendpage() internal: do no apply policy.
* `"SENDPAGE_NOTLAST"`: sendpage() internal: not the last page.
* `"BATCH"`: sendmmsg(): more messages coming.
* `"EOF"`: n/a.
* `"NO_SHARED_FRAGS"`: sendpage() internal: page frags are not shared.
* `"SENDPAGE_DECRYPTED"`: sendpage() internal: page may carry plain text and require encryption.
* `"ZEROCOPY"`: Use user data in kernel path.
* `"FASTOPEN"`: Send data in TCP SYN.
* `"CMSG_CLOEXEC"`: Set close\_on\_exec for file descriptor received through SCM\_RIGHTS.
#### `sock:bind(addr [, port])`
_sock:bind()_ binds the socket `sock` to a given address.
If the `sock` address family is `af.INET`, then it expects the following arguments:
* `addr`: `integer` describing host IPv4 address.
* `port`: `integer` describing host IPv4 port.
Otherwise:
* `addr`: [packed string](https://www.lua.org/manual/5.4/manual.html#6.4.2) describing host address.
#### `sock:listen([backlog])`
_sock:listen()_ moves the socket `sock` to listening state.
* `backlog`: pending connections queue size.
If omitted, it uses
[SOMAXCONN](https://elixir.bootlin.com/linux/latest/source/include/linux/socket.h#L296)
as default.
#### `sock:accept([flags])`
_sock:accept()_ accepts a connection on socket `sock`.
It returns a new `socket` object.
The available _flags_ are present on the
[socket.sock](https://github.com/luainkernel/lunatik#socketsock) table.
#### `sock:connect(addr [, port] [, flags])`
_sock:connect()_ connects the socket `sock` to the address `addr`.
If the `sock` address family is `af.INET`, then it expects the following arguments:
* `addr`: `integer` describing the destination IPv4 address.
* `port`: `integer` describing the destination IPv4 port.
Otherwise:
* `addr`: [packed string](https://www.lua.org/manual/5.4/manual.html#6.4.2) describing the destination address.
The available _flags_ are present on the
[socket.sock](https://github.com/luainkernel/lunatik#socketsock) table.
For datagram sockets, `addr` is the address to which datagrams are sent
by default, and the only address from which datagrams are received.
For stream sockets, attempts to connect to `addr`.
#### `sock:getsockname()`
_sock:getsockname()_ get the address which the socket `sock` is bound.
If the `sock` address family is `af.INET`, then it returns the following:
* `addr`: `integer` describing the bounded IPv4 address.
* `port`: `integer` describing the bounded IPv4 port.
Otherwise:
* `addr`: [packed string](https://www.lua.org/manual/5.4/manual.html#6.4.2) describing the bounded address.
#### `sock:getpeername()`
_sock:getpeername()_ get the address which the socket `sock` is connected.
If the `sock` address family is `af.INET`, then it returns the following:
* `addr`: `integer` describing the peer's IPv4 address.
* `port`: `integer` describing the peer's IPv4 port.
Otherwise:
* `addr`: [packed string](https://www.lua.org/manual/5.4/manual.html#6.4.2) describing the peer's address.
### socket.inet
The `socket.inet` library provides support for high-level IPv4 sockets.
#### `inet.tcp()`
_inet.tcp()_ creates a new `socket` using
[af.INET](https://github.com/luainkernel/lunatik#socketaf) address family,
[sock.STREAM](https://github.com/luainkernel/lunatik#socketsock) type
and
[ipproto.TCP](https://github.com/luainkernel/lunatik#socketipproto) protocol.
It overrides `socket` methods to use addresses as _numbers-and-dots notation_
(e.g., `"127.0.0.1"`), instead of integers.
#### `inet.udp()`
_inet.udp()_ creates a new `socket` using
[af.INET](https://github.com/luainkernel/lunatik#socketaf) address family,
[sock.DGRAM](https://github.com/luainkernel/lunatik#socketsock) type
and
[ipproto.UDP](https://github.com/luainkernel/lunatik#socketipproto) protocol.
It overrides `socket` methods to use addresses as _numbers-and-dots notation_
(e.g., `"127.0.0.1"`), instead of integers.
##### `udp:receivefrom(length [, flags])`
_udp:receivefrom()_ is just an alias to `sock:receive(length, flags, true)`.
### rcu
The `rcu` library provides support for the kernel
[Read-copy update (RCU)](https://lwn.net/Articles/262464/)
synchronization mechanism.
This library was inspired by
[Caio Messias](https://github.com/cmessias)'
[GSoC project](https://summerofcode.withgoogle.com/archive/2018/projects/5736202426646528).
#### `rcu.table([size])`
_rcu.table()_ creates a new `rcu.table` object
which binds the kernel [generic hash table](https://lwn.net/Articles/510202/).
This function receives as argument the number of buckets rounded up to the next power of 2.
The default size is `1024`.
Key must be a string and value must be a Lunatik object or nil.
### thread
The `thread` library provides support for the
[kernel thread primitives](https://lwn.net/Articles/65178/).
#### `thread.run(runtime, name)`
_thread.run()_ creates a new `thread` object and wakes it up.
This function receives the following arguments:
* `runtime`: the
[runtime environment](https://github.com/luainkernel/lunatik#lunatikruntimescript--sleep)
for running a task in the created kernel thread.
The task must be specified by returning a function on the script loaded
in the `runtime` environment.
* `name`: string representing the name for the thread (e.g., as shown on `ps`).
#### `thread.shouldstop()`
_thread.shouldstop()_ returns `true` if
[thread.stop()](https://github.com/luainkernel/lunatik#threadstopthrd-thrdstop)
was called; otherwise, it returns `false`.
#### `thread.current()`
_thread.current()_ returns a `thread` object representing the current task.
#### `thrd:stop()`
_thrd:stop()_ sets
[thread.shouldstop()](https://github.com/luainkernel/lunatik#threadshouldstop)
on the thread `thrd` to return true, wakes `thrd`, and waits for it to exit.
#### `thrd:task()`
_thrd:task()_ returns a table containing the task information of this `thread`
(e.g., "cpu", "command", "pid" and "tgid").
### fib
The `fib` library provides support for the
[kernel Forwarding Information Base](https://thermalcircle.de/doku.php?id=blog:linux:routing_decisions_in_the_linux_kernel_1_lookup_packet_flow).
#### `fib.newrule(table, priority)`
_fib.newrule()_ binds the kernel
[fib_nl_newrule](https://elixir.bootlin.com/linux/latest/source/include/net/fib_rules.h#L182)
API;
it creates a new FIB rule that matches the specified routing _table_
with the specified _priorioty_.
This function is similar to the user-space command
[ip rule add](https://datahacker.blog/industry/technology-menu/networking/iptables/follow-the-ip-rules)
provided by [iproute2](https://wiki.linuxfoundation.org/networking/iproute2).
#### `fib.delrule(table, priority)`
_fib.delrule()_ binds the kernel
[fib_nl_delrule](https://elixir.bootlin.com/linux/latest/source/include/net/fib_rules.h#L184)
API;
it removes a FIB rule that matches the specified routing _table_
with the specified _priorioty_.
This function is similar to the user-space command
[ip rule del](https://datahacker.blog/industry/technology-menu/networking/iptables/follow-the-ip-rules)
provided by [iproute2](https://wiki.linuxfoundation.org/networking/iproute2).
### data
The `data` library provides support for binding the system memory to Lua.
#### `data.new(size)`
_data.new()_ creates a new `data` object which allocates `size` bytes.
#### `d:getnumber(offset)`
_d:getnumber()_ extracts a [lua\_Integer](https://www.lua.org/manual/5.4/manual.html#lua_Integer)
from the memory referenced by a `data` object and a byte `offset`,
starting from zero.
#### `d:setnumber(offset, number)`
_d:setnumber()_ insert a [lua\_Integer](https://www.lua.org/manual/5.4/manual.html#lua_Integer)
`number` into the memory referenced by a `data` object and a byte `offset`,
starting from zero.
#### `d:getbyte(offset)`
_d:getbyte()_ extracts a byte
from the memory referenced by a `data` object and a byte `offset`,
starting from zero.
#### `d:setbyte(offset, byte)`
_d:setbyte()_ insert a byte
into the memory referenced by a `data` object and a byte `offset`,
starting from zero.
#### `d:getstring(offset[, length])`
_d:getstring()_ extracts a string with `length` bytes
from the memory referenced by a `data` object and a byte `offset`,
starting from zero. If `length` is omitted, it extracts all bytes
from `offset` to the end of the `data`.
#### `d:setstring(offset, s)`
_d:setstring()_ insert the string `s`
into the memory referenced by a `data` object and a byte `offset`,
starting from zero.
#### `d:getint8(offset)`
_d:getint8(d, offset)_ extracts a signed 8-bit integer
from the memory referenced by a `data` object and a byte `offset`,
starting from zero.
#### `d:setint8(offset, number)`
_d:setint8()_ inserts a signed 8-bit number
into the memory referenced by a `data` object and a byte `offset`,
starting from zero.
#### `d:getuint8(offset)`
_d:getuint8()_ extracts an unsigned 8-bit integer
from the memory referenced by a `data` object and a byte `offset`,
starting from zero.
#### `d:setuint8(offset, number)`
_d:setuint8()_ inserts an unsigned 8-bit number
into the memory referenced by a `data` object and a byte `offset`,
starting from zero.
#### `d:getint16(offset)`
_d:getint16()_ extracts a signed 16-bit integer
from the memory referenced by a `data` object and a byte `offset`,
starting from zero.
#### `d:setint16(offset, number)`
_d:setint16()_ inserts a signed 16-bit number
into the memory referenced by a `data` object and a byte `offset`,
starting from zero.
#### `d:getuint16(offset)`
_d:getuint16()_ extracts an unsigned 16-bit integer
from the memory referenced by a `data` object and a byte `offset`,
starting from zero.
#### `d:setuint16(offset, number)`
_d:setuint16()_ inserts an unsigned 16-bit number
into the memory referenced by a `data` object and a byte `offset`,
starting from zero.
#### `d:getint32(offset)`
_d:getint32()_ extracts a signed 32-bit integer
from the memory referenced by a `data` object and a byte `offset`,
starting from zero.
#### `d:setint32(offset, number)`
_d:setint32()_ inserts a signed 32-bit number
into the memory referenced by a `data` object and a byte `offset`,
starting from zero.
#### `d:getuint32(offset)`
_d:getuint32()_ extracts an unsigned 32-bit integer
from the memory referenced by a `data` object and a byte `offset`,
starting from zero.
#### `d:setuint32(offset, number)`
_d:setuint32()_ inserts an unsigned 32-bit number
into the memory referenced by a `data` object and a byte `offset`,
starting from zero.
#### `d:getint64(offset)`
_d:getint64()_ extracts a signed 64-bit integer
from the memory referenced by a `data` object and a byte `offset`,
starting from zero.
#### `d:setint64(offset, number)`
_d:setint64()_ inserts a signed 64-bit number
into the memory referenced by a `data` object and a byte `offset`,
starting from zero.
### probe
The `probe` library provides support for
[kernel probes](https://docs.kernel.org/trace/kprobes.html).
#### `probe.new(symbol|address, handlers)`
_probe.new()_ returns a new `probe` object for monitoring a kernel `symbol` (string) or `address` (light userdata)
and installs its `handlers` in the system.
The `handler` **must** be defined as a table containing the following field:
* `pre`: function to be called before the probed instruction.
It receives the `symbol` or `address`,
followed by a closure that may be called to
[show the CPU registers and stack](https://elixir.bootlin.com/linux/v5.6.19/source/include/linux/sched/debug.h#L26)
in the system log.
* `post`: function to be called after the probed instruction.
It receives the `symbol` or `address`,
followed by a closure that may be called to
[show the CPU registers and stack](https://elixir.bootlin.com/linux/v5.6.19/source/include/linux/sched/debug.h#L26)
in the system log.
#### `p:stop()`
_p:stop()_ removes the `probe` handlers from the system.
#### `p:enable(bool)`
_p:enable()_ enables or disables the `probe` handlers, accordingly to `bool`.
### syscall
The `syscall` library provides support for system call addresses and numbers.
#### `syscall.address(number)`
_syscall.address()_ returns the system call address (light userdata) referenced by the given `number`.
#### `syscall.number(name)`
_syscall.number()_ returns the system call number referenced by the given `name`.
### syscall.table
The `syscall.table` library provides support for translating system call names to addresses (light userdata).
### xdp
The `xdp` library provides support for the kernel
[eXpress Data Path (XDP)](https://prototype-kernel.readthedocs.io/en/latest/networking/XDP/)
subsystem.
This library was inspired by
[Victor Nogueira](https://github.com/VictorNogueiraRio/linux)'s
[GSoC project](https://victornogueirario.github.io/xdplua/).
#### `xdp.attach(callback)`
_xdp.attach()_ registers a `callback` function to the current `runtime`
to be called from an XDP/eBPF program whenever it calls
[bpf_luaxdp_run](lib/luaxdp.c#L106)
[kfunc](https://docs.kernel.org/bpf/kfuncs.html).
This `callback` receives the following arguments:
* `buffer`: a `data` object representing the network buffer.
* `argument`: a `data` object containing the argument passed by the XDP/eBPF program.
The `callback` function might return the values defined by the
[xdp.action](https://github.com/luainkernel/lunatik#xdpaction) table.
#### `xdp.detach()`
_xdp.detach()_ unregisters the `callback` associated with the current `runtime`, if any.
#### `xdp.action`
_xdp.action_ is a table that exports
[xdp_action](https://elixir.bootlin.com/linux/v6.4/source/include/uapi/linux/bpf.h#L6187)
flags to Lua.
* `"ABORTED"`: Indicates that the XDP program aborted, typically due to an error.
* `"DROP"`: Specifies that the packet should be dropped, discarding it entirely.
* `"PASS"`: Allows the packet to pass through to the Linux network stack.
* `"TX"`: Transmits the packet back out on the same interface it was received.
* `"REDIRECT"`: Redirects the packet to another interface or processing context.
### xtable
The `xtable` library provides support for developing netfilter [xtable extensions](https://inai.de/projects/xtables-addons/).
#### `xtable.match(opts)`
_xtable.match()_ returns a new [xtable](https://inai.de/projects/xtables-addons/) object for match extensions.
This function receives the following arguments:
* `opts`: a table containing the following fields:
* `name`: string representing the xtable extension name.
* `revision`: integer representing the xtable extension revision.
* `family`: address family, one of [netfilter.family](https://github.com/luainkernel/lunatik#netfilterfamily).
* `proto`: protocol number, one of [socket.ipproto](https://github.com/luainkernel/lunatik#socketipproto).
* `hooks` : hook to attach the extension to, one value from either of the hooks table - [netfilter.inet_hooks](https://github.com/luainkernel/lunatik#netfilterinet_hooks), [netfilter.bridge_hooks](https://github.com/luainkernel/lunatik#netfilterbridge_hooks) and [netfilter.arp_hooks](https://github.com/luainkernel/lunatik#netfilterarp_hooks) (Note: [netfilter.netdev_hooks](https://github.com/luainkernel/lunatik#netfilternetdev_hooks) is not available for legacy x_tables). (E.g - `1 << inet_hooks.LOCAL_OUT`).
* `match` : function to be called for matching packets. It receives the following arguments:
* `skb` (readonly): a `data` object representing the socket buffer.
* `par`: a table containing `hotdrop`, `thoff` (transport header offset) and `fragoff` (fragment offset) fields.
* `userargs` : a lua string passed from the userspace xtable module.
* The function must return `true` if the packet matches the extension; otherwise, it must return `false`.
* `checkentry`: function to be called for checking the entry. This function receives `userargs` as its argument.
* `destroy`: function to be called for destroying the xtable extension. This function receives `userargs` as its argument.
#### `xtable.target(opts)`
_xtable.target()_ returns a new [xtable](https://inai.de/projects/xtables-addons/) object for target extension.
This function receives the following arguments:
* `opts`: a table containing the following fields:
* `name`: string representing the xtable extension name.
* `revision`: integer representing the xtable extension revision.
* `family`: address family, one of [netfilter.family](https://github.com/luainkernel/lunatik#netfilterfamily).
* `proto`: protocol number, one of [socket.ipproto](https://github.com/luainkernel/lunatik#socketipproto).
* `hooks` : hook to attach the extension to, one value from either of the hooks table - [netfilter.inet_hooks](https://github.com/luainkernel/lunatik#netfilterinet_hooks), [netfilter.bridge_hooks](https://github.com/luainkernel/lunatik#netfilterbridge_hooks) and [netfilter.arp_hooks](https://github.com/luainkernel/lunatik#netfilterarp_hooks) (Note: [netfilter.netdev_hooks](https://github.com/luainkernel/lunatik#netfilternetdev_hooks) is not available for legacy x_tables). (E.g - `1 << inet_hooks.LOCAL_OUT`).
* `target` : function to be called for targeting packets. It receives the following arguments:
* `skb`: a `data` object representing the socket buffer.
* `par` (readonly): a table containing `hotdrop`, `thoff` (transport header offset) and `fragoff` (fragment offset) fields.
* `userargs` : a lua string passed from the userspace xtable module.
* The function must return one of the values defined by the [netfilter.action](https://github.com/luainkernel/lunatik#netfilteraction) table.
* `checkentry`: function to be called for checking the entry. This function receives `userargs` as its argument.
* `destroy`: function to be called for destroying the xtable extension. This function receives `userargs` as its argument.
### netfilter
The `netfilter` library provides support for the [new netfilter hook](https://www.netfilter.org/documentation/HOWTO/netfilter-hacking-HOWTO-4.html#ss4.6) system.
#### `netfilter.register(ops)`
_netfilter.register()_ registers a new netfilter hook with the given `ops` table.
This function receives the following arguments:
* `ops`: a table containing the following fields:
* `pf`: protocol family, one of [netfilter.family](https://github.com/luainkernel/lunatik#netfilterfamily)
* `hooknum`: hook to attach the filter to, one value from either of the hooks table - [netfilter.inet_hooks](https://github.com/luainkernel/lunatik#netfilterinet_hooks), [netfilter.bridge_hooks](https://github.com/luainkernel/lunatik#netfilterbridge_hooks), [netfilter.arp_hooks](https://github.com/luainkernel/lunatik#netfilterarp_hooks) and [netfilter.netdev_hooks](https://github.com/luainkernel/lunatik#netfilternetdev_hooks). (E.g - `inet_hooks.LOCAL_OUT + 11`).
* `priority`: priority of the hook. One of the values from the [netfilter.ip_priority](https://github.com/luainkernel/lunatik#netfilterip_priority) or [netfilter.bridge_priority](https://github.com/luainkernel/lunatik#netfilterbridge_priority) tables.
* `hook`: function to be called for the hook. It receives the following arguments:
* `skb`: a `data` object representing the socket buffer.
* The function must return one of the values defined by the [netfilter.action](https://github.com/luainkernel/lunatik#netfilteraction).
#### `netfilter.family`
_netfilter.family_ is a table that exports
address families to Lua.
* `"UNSPEC"`: Unspecified.
* `"INET"`: Internet Protocol version 4.
* `"IPV4"`: Internet Protocol version 4.
* `"IPV6"`: Internet Protocol version 6.
* `"ARP"`: Address Resolution Protocol.
* `"NETDEV"`: Device ingress and egress path
* `"BRIDGE"`: Ethernet Bridge.
#### `netfilter.action`
_netfilter.action_ is a table that exports
netfilter actions to Lua.
* `"DROP"`: `NF_DROP`. The packet is dropped. It is not forwarded, processed, or seen by any other network layer.
* `"ACCEPT"`: `NF_ACCEPT`. The packet is accepted and passed to the next step in the network processing chain.
* `"STOLEN"`: `NF_STOLEN`. The packet is taken by the handler, and processing stops.
* `"QUEUE"`: `NF_QUEUE`. The packet is queued for user-space processing.
* `"REPEAT"`: `NF_REPEAT`. The packet is sent through the hook chain again.
* `"STOP"`: `NF_STOP`. Processing of the packet stops.
* `"CONTINUE"`: `XT_CONTINUE`. Return the packet should continue traversing the rules within the same table.
* `"RETURN"`: `XT_RETURN`. Return the packet to the previous chain.
#### `netfilter.inet_hooks`
_netfilter.inet_hooks_ is a table that exports
inet netfilter hooks to Lua.
* `"PRE_ROUTING"`: `NF_INET_PRE_ROUTING`. The packet is received by the network stack.
* `"LOCAL_IN"`: `NF_INET_LOCAL_IN`. The packet is destined for the local system.
* `"FORWARD"`: `NF_INET_FORWARD`. The packet is to be forwarded to another host.
* `"LOCAL_OUT"`: `NF_INET_LOCAL_OUT`. The packet is generated by the local system.
* `"POST_ROUTING"`: `NF_INET_POST_ROUTING`. The packet is about to be sent out.
#### `netfilter.bridge_hooks`
_netfilter.bridge_hooks_ is a table that exports
bridge netfilter hooks to Lua.
* `"PRE_ROUTING"`: `NF_BR_PRE_ROUTING`. First hook invoked, runs before forward database is consulted.
* `"LOCAL_IN"`: `NF_BR_LOCAL_IN`. Invoked for packets destined for the machine where the bridge was configured on.
* `"FORWARD"`: `NF_BR_FORWARD`. Called for frames that are bridged to a different port of the same logical bridge device.
* `"LOCAL_OUT"`: `NF_BR_LOCAL_OUT`. Called for locally originating packets that will be transmitted via the bridge.
* `"POST_ROUTING"`: `NF_BR_POST_ROUTING`. Called for all locally generated packets and all bridged packets
#### `netfilter.arp_hooks`
_netfilter.arp_hooks_ is a table that exports
arp netfilter hooks to Lua.
* `"IN"`: `NF_ARP_IN`. The packet is received by the network stack.
* `"OUT"`: `NF_ARP_OUT`. The packet is generated by the local system.
* `"FORWARD"`: `NF_ARP_FORWARD`. The packet is to be forwarded to another host.
#### `netfilter.netdev_hooks`
_netfilter.netdev_hooks_ is a table that exports
netdev netfilter hooks to Lua.
* `"INGRESS"`: `NF_NETDEV_INGRESS`. The packet is received by the network stack.
* `"EGRESS"`: `NF_NETDEV_EGRESS`. The packet is generated by the local system.
#### `netfilter.ip_priority`
_netfilter.ip_priority_ is a table that exports
netfilter IPv4/IPv6 priority levels to Lua.
* `"FIRST"`: `NF_IP_PRI_FIRST`
* `"RAW_BEFORE_DEFRAG"`: `NF_IP_PRI_RAW_BEFORE_DEFRAG`
* `"CONNTRACK_DEFRAG"`: `NF_IP_PRI_CONNTRACK_DEFRAG`
* `"RAW"`: `NF_IP_PRI_RAW`
* `"SELINUX_FIRST"`: `NF_IP_PRI_SELINUX_FIRST`
* `"CONNTRACK"`: `NF_IP_PRI_CONNTRACK`
* `"MANGLE"`: `NF_IP_PRI_MANGLE`
* `"NAT_DST"`: `NF_IP_PRI_NAT_DST`
* `"FILTER"`: `NF_IP_PRI_FILTER`
* `"SECURITY"`: `NF_IP_PRI_SECURITY`
* `"NAT_SRC"`: `NF_IP_PRI_NAT_SRC`
* `"SELINUX_LAST"`: `NF_IP_PRI_SELINUX_LAST`
* `"CONNTRACK_HELPER"`: `NF_IP_PRI_CONNTRACK_HELPER`
* `"LAST"`: `NF_IP_PRI_LAST`
#### `netfilter.bridge_priority`
_netfilter.bridge_priority_ is a table that exports
netfilter bridge priority levels to Lua.
* `"FIRST"`: `NF_BR_PRI_FIRST`
* `"NAT_DST_BRIDGED"`: `NF_BR_PRI_NAT_DST_BRIDGED`
* `"FILTER_BRIDGED"`: `NF_BR_PRI_FILTER_BRIDGED`
* `"BRNF"`: `NF_BR_PRI_BRNF`
* `"NAT_DST_OTHER"`: `NF_BR_PRI_NAT_DST_OTHER`
* `"FILTER_OTHER"`: `NF_BR_PRI_FILTER_OTHER`
* `"NAT_SRC"`: `NF_BR_PRI_NAT_SRC`
* `"LAST"`: `NF_BR_PRI_LAST`
### luaxt
The `luaxt` [userspace library](usr/lib/xtable) provides support for generating userspace code for [xtable extensions](https://inai.de/projects/xtables-addons/).
To build the library, the following steps are required:
1. Go to `usr/lib/xtable` and create a `libxt_<ext_name>.lua` file.
2. Register your callbacks for the xtable extension by importing the library (`luaxt`) in the created file.
3. Run `LUAXTABLE_MODULE=<ext_name> make` to build the extension and `LUAXTABLE_MODULE=<ext_name> make install` (as root) to install the userspace plugin to the system.
Now load the extension normally using `iptables`.
#### `luaxt.match(opts)`
_luaxt.match()_ returns a new [luaxt](https://inai.de/projects/xtables-addons/) object for match extensions.
This function receives the following arguments:
* `opts`: a table containing the following fields:
* `revision`: integer representing the xtable extension revision (**must** be same as used in corresponding kernel extension).
* `family`: address family, one of [luaxt.family](https://github.com/luainkernel/lunatik#luaxtfamily)
* `help`: function to be called for displaying help message for the extension.
* `init`: function to be called for initializing the extension. This function receives an `par` table that can be used to set `userargs`. (`par.userargs = "mydata"`)
* `print`: function to be called for printing the arguments. This function recevies `userargs` set by the `init` or `parse` function.
* `save`: function to be called for saving the arguments. This function recevies `userargs` set by the `init` or `parse` function.
* `parse`: function to be called for parsing the command line arguments. This function receives an `par` table that can be used to set `userargs` and `flags`. (`par.userargs = "mydata"`)
* `final_check`: function to be called for final checking of the arguments. This function receives `flags` set by the `parse` function.
#### `luaxt.target(opts)`
_luaxt.target()_ returns a new [luaxt](https://inai.de/projects/xtables-addons/) object for target extensions.
This function receives the following arguments:
* `opts`: a table containing the following fields:
* `revision`: integer representing the xtable extension revision (**must** be same as used in corresponding kernel extension).
* `family`: address family, one of [luaxt.family](https://github.com/luainkernel/lunatik#luaxtfamily)
* `help`: function to be called for displaying help message for the extension.
* `init`: function to be called for initializing the extension. This function receives an `par` table that can be used to set `userargs`. (`par.userargs = "mydata"`)
* `print`: function to be called for printing the arguments. This function recevies `userargs` set by the `init` or `parse` function.
* `save`: function to be called for saving the arguments. This function recevies `userargs` set by the `init` or `parse` function.
* `parse`: function to be called for parsing the command line arguments. This function receives an `par` table that can be used to set `userargs` and `flags`. (`par.userargs = "mydata"`)
* `final_check`: function to be called for final checking of the arguments. This function receives `flags` set by the `parse` function.
#### `luaxt.family`
_luaxt.family_ is a table that exports
address families to Lua.
* `"UNSPEC"`: Unspecified.
* `"INET"`: Internet Protocol version 4.
* `"IPV4"`: Internet Protocol version 4.
* `"IPV6"`: Internet Protocol version 6.
* `"ARP"`: Address Resolution Protocol.
* `"NETDEV"`: Device ingress and egress path
* `"BRIDGE"`: Ethernet Bridge.
### `completion`
The `completion` library provides support for the [kernel completion primitives](https://docs.kernel.org/scheduler/completion.html).
Task completion is a synchronization mechanism used to coordinate the execution of multiple threads, similar to `pthread_barrier`, it allows threads to wait for a specific event to occur before proceeding, ensuring certain tasks are complete in a race-free manner.
#### `completion.new()`
_completion.new()_ creates a new `completion` object.
#### `c:complete()`
_c:complete()_ signals a single thread waiting on this completion.
#### `c:wait([timeout])`
_c:wait()_ waits for completion of a task until the specified timeout expires.
The timeout is specified in milliseconds. If the `timeout` parameter is omitted, it waits indefinitely. Passing a timeout value less than zero results in undefined behavior.
Threads waiting for events can be interrupted by signals, for example, such as when `thread.stop` is invoked.
Therefore, this function can return in three ways:
* If it succeeds, it returns `true`
* If the timeout is reached, it returns `nil, "timeout"`
* If the task is interrupted, it returns `nil, "interrupt"`
# Examples
### spyglass
[spyglass](examples/spyglass.lua)
is a kernel script that implements a _keylogger_ inspired by the
[spy](https://github.com/jarun/spy) kernel module.
This kernel script logs the _keysym_ of the pressed keys in a device (`/dev/spyglass`).
If the _keysym_ is a printable character, `spyglass` logs the _keysym_ itself;
otherwise, it logs a mnemonic of the ASCII code, (e.g., `<del>` stands for `127`).
#### Usage
```
sudo make examples_install # installs examples
sudo lunatik run examples/spyglass # runs spyglass
sudo tail -f /dev/spyglass # prints the key log
sudo sh -c "echo 'enable=false' > /dev/spyglass" # disable the key logging
sudo sh -c "echo 'enable=true' > /dev/spyglass" # enable the key logging
sudo sh -c "echo 'net=127.0.0.1:1337' > /dev/spyglass" # enable network support
nc -lu 127.0.0.1 1337 & # listen to UDP 127.0.0.1:1337
sudo tail -f /dev/spyglass # sends the key log through the network
```
### keylocker
[keylocker](examples/keylocker.lua)
is a kernel script that implements
[Konami Code](https://en.wikipedia.org/wiki/Konami_Code)
for locking and unlocking the console keyboard.
When the user types `↑ ↑ ↓ ↓ ← → ← → LCTRL LALT`,
the keyboard will be _locked_; that is, the system will stop processing any key pressed
until the user types the same key sequence again.
#### Usage
```
sudo make examples_install # installs examples
sudo lunatik run examples/keylocker # runs keylocker
<↑> <↑> <↓> <↓> <←> <→> <←> <→> <LCTRL> <LALT> # locks keyboard
<↑> <↑> <↓> <↓> <←> <→> <←> <→> <LCTRL> <LALT> # unlocks keyboard
```
### tap
[tap](examples/tap.lua)
is a kernel script that implements a _sniffer_ using `AF_PACKET` socket.
It prints destination and source MAC addresses followed by Ethernet type and the frame size.
#### Usage
```
sudo make examples_install # installs examples
sudo lunatik run examples/tap # runs tap
cat /dev/tap
```
### shared
[shared](examples/shared.lua)
is a kernel script that implements an in-memory key-value store using
[rcu](https://github.com/luainkernel/lunatik#rcu),
[data](https://github.com/luainkernel/lunatik#data),
[socket](https://github.com/luainkernel/lunatik#socket) and
[thread](https://github.com/luainkernel/lunatik#thread).
#### Usage
```
sudo make examples_install # installs examples
sudo lunatik spawn examples/shared # spawns shared
nc 127.0.0.1 90 # connects to shared
foo=bar # assigns "bar" to foo
foo # retrieves foo
bar
^C # finishes the connection
```
### echod
[echod](examples/echod)
is an echo server implemented as kernel scripts.
#### Usage
```
sudo make examples_install # installs examples
sudo lunatik spawn examples/echod/daemon # runs echod
nc 127.0.0.1 1337
hello kernel!
hello kernel!
```
### systrack
[systrack](examples/systrack.lua)
is a kernel script that implements a device driver to monitor system calls.
It prints the amount of times each [system call](examples/systrack.lua#L29)
was called since the driver has been installed.
#### Usage
```
sudo make examples_install # installs examples
sudo lunatik run examples/systrack # runs systracker
cat /dev/systrack
writev: 0
close: 1927
write: 1085
openat: 2036
read: 4131
readv: 0
```
### filter
[filter](examples/filter) is a kernel extension composed by
a XDP/eBPF program to filter HTTPS sessions and
a Lua kernel script to filter [SNI](https://datatracker.ietf.org/doc/html/rfc3546#section-3.1) TLS extension.
This kernel extension drops any HTTPS request destinated to a
[blacklisted](examples/filter/sni.lua#L35) server.
#### Usage
Compile and install `libbpf`, `libxdp` and `xdp-loader`:
```sh
mkdir -p "${LUNATIK_DIR}" ; cd "${LUNATIK_DIR}" # LUNATIK_DIR must be set to the same value as above (Setup section)
git clone --depth 1 --recurse-submodules https://github.com/xdp-project/xdp-tools.git
cd xdp-tools/lib/libbpf/src
make
sudo DESTDIR=/ make install
cd ../../../
make libxdp
cd xdp-loader
make
sudo make install
```
Come back to this repository, install and load the filter:
```sh
cd ${LUNATIK_DIR}/lunatik # cf. above
sudo make btf_install # needed to export the 'bpf_luaxdp_run' kfunc
sudo make examples_install # installs examples
make ebpf # builds the XDP/eBPF program
sudo make ebpf_install # installs the XDP/eBPF program
sudo lunatik run examples/filter/sni false # runs the Lua kernel script
sudo xdp-loader load -m skb <ifname> https.o # loads the XDP/eBPF program
```
For example, testing is easy thanks to [docker](https://www.docker.com).
Assuming docker is installed and running:
- in a terminal:
```sh
sudo xdp-loader load -m skb docker0 https.o
sudo journalctl -ft kernel
```
- in another one:
```sh
docker run --rm -it alpine/curl https://ebpf.io
```
The system logs (in the first terminal) should display `filter_sni: ebpf.io DROP`, and the
`docker run…` should return `curl: (35) OpenSSL SSL_connect: SSL_ERROR_SYSCALL in connection to ebpf.io:443`.
### filter in MoonScript
[This other sni filter](https://github.com/luainkernel/snihook) uses netfilter api.
### dnsblock
[dnsblock](examples/dnsblock) is a kernel script that uses the lunatik xtable library to filter DNS packets.
This script drops any outbound DNS packet with question matching the blacklist provided by the user.
#### Usage
1. Using legacy iptables
```
sudo make examples_install # installs examples
cd examples/dnsblock
make # builds the userspace extension for netfilter
sudo make install # installs the extension to Xtables directory
sudo lunatik run examples/dnsblock/dnsblock false # runs the Lua kernel script
sudo iptables -A OUTPUT -m dnsblock -j DROP # this initiates the netfilter framework to load our extension
```
2. Using new netfilter framework ([luanetfilter](https://github.com/luainkernel/lunatik#netfilter))
```
sudo make examples_install # installs examples
sudo lunatik run examples/dnsblock/nf_dnsblock false # runs the Lua kernel script
```
### dnsdoctor
[dnsdoctor](examples/dnsdoctor) is a kernel script that uses the lunatik xtable library to change the DNS response
from Public IP to a Private IP if the destination IP matches the one provided by the user. For example, if the user
wants to change the DNS response from `192.168.10.1` to `10.1.2.3` for the domain `lunatik.com` if the query is being sent to `10.1.1.2` (a private client), this script can be used.
#### Usage
1. Using legacy iptables
```
sudo make examples_install # installs examples
cd examples/dnsdoctor
setup.sh # sets up the environment
# test the setup, a response with IP 192.168.10.1 should be returned
dig lunatik.com
# run the Lua kernel script
sudo lunatik run examples/dnsdoctor/dnsdoctor false
# build and install the userspace extension for netfilter
make
sudo make install
# add rule to the mangle table
sudo iptables -t mangle -A PREROUTING -p udp --sport 53 -j dnsdoctor
# test the setup, a response with IP 10.1.2.3 should be returned
dig lunatik.com
# cleanup
sudo iptables -t mangle -D PREROUTING -p udp --sport 53 -j dnsdoctor # remove the rule
sudo lunatik unload
cleanup.sh
```
2. Using new netfilter framework ([luanetfilter](https://github.com/luainkernel/lunatik#netfilter))
```
sudo make examples_install # installs examples
examples/dnsdoctor/setup.sh # sets up the environment
# test the setup, a response with IP 192.168.10.1 should be returned
dig lunatik.com
# run the Lua kernel script
sudo lunatik run examples/dnsdoctor/nf_dnsdoctor false
# test the setup, a response with IP 10.1.2.3 should be returned
dig lunatik.com
# cleanup
sudo lunatik unload
examples/dnsdoctor/cleanup.sh
```
## References
* [Scripting the Linux Routing Table with Lua](https://netdevconf.info/0x17/sessions/talk/scripting-the-linux-routing-table-with-lua.html)
* [Lua no Núcleo](https://www.youtube.com/watch?v=-ufBgy044HI) (Portuguese)
* [Linux Network Scripting with Lua](https://legacy.netdevconf.info/0x14/session.html?talk-linux-network-scripting-with-lua)
* [Scriptables Operating Systems with Lua](https://www.netbsd.org/~lneto/dls14.pdf)
## License
Lunatik is dual-licensed under [MIT](LICENSE-MIT) or [GPL-2.0-only](LICENSE-GPL).
[Lua](https://github.com/luainkernel/lua) submodule is licensed under MIT.
For more details, see its [Copyright Notice](https://github.com/luainkernel/lua/blob/lunatik/lua.h#L530-L556).
[Klibc](https://github.com/luainkernel/klibc) submodule is dual-licensed under BSD 3-Clause or GPL-2.0-only.
For more details, see its [LICENCE](https://github.com/luainkernel/klibc/blob/lunatik/usr/klibc/LICENSE) file.
", Assign "at most 3 tags" to the expected json: {"id":"9567","tags":[]} "only from the tags list I provide: [{"id":77,"name":"3d"},{"id":89,"name":"agent"},{"id":17,"name":"ai"},{"id":54,"name":"algorithm"},{"id":24,"name":"api"},{"id":44,"name":"authentication"},{"id":3,"name":"aws"},{"id":27,"name":"backend"},{"id":60,"name":"benchmark"},{"id":72,"name":"best-practices"},{"id":39,"name":"bitcoin"},{"id":37,"name":"blockchain"},{"id":1,"name":"blog"},{"id":45,"name":"bundler"},{"id":58,"name":"cache"},{"id":21,"name":"chat"},{"id":49,"name":"cicd"},{"id":4,"name":"cli"},{"id":64,"name":"cloud-native"},{"id":48,"name":"cms"},{"id":61,"name":"compiler"},{"id":68,"name":"containerization"},{"id":92,"name":"crm"},{"id":34,"name":"data"},{"id":47,"name":"database"},{"id":8,"name":"declarative-gui "},{"id":9,"name":"deploy-tool"},{"id":53,"name":"desktop-app"},{"id":6,"name":"dev-exp-lib"},{"id":59,"name":"dev-tool"},{"id":13,"name":"ecommerce"},{"id":26,"name":"editor"},{"id":66,"name":"emulator"},{"id":62,"name":"filesystem"},{"id":80,"name":"finance"},{"id":15,"name":"firmware"},{"id":73,"name":"for-fun"},{"id":2,"name":"framework"},{"id":11,"name":"frontend"},{"id":22,"name":"game"},{"id":81,"name":"game-engine "},{"id":23,"name":"graphql"},{"id":84,"name":"gui"},{"id":91,"name":"http"},{"id":5,"name":"http-client"},{"id":51,"name":"iac"},{"id":30,"name":"ide"},{"id":78,"name":"iot"},{"id":40,"name":"json"},{"id":83,"name":"julian"},{"id":38,"name":"k8s"},{"id":31,"name":"language"},{"id":10,"name":"learning-resource"},{"id":33,"name":"lib"},{"id":41,"name":"linter"},{"id":28,"name":"lms"},{"id":16,"name":"logging"},{"id":76,"name":"low-code"},{"id":90,"name":"message-queue"},{"id":42,"name":"mobile-app"},{"id":18,"name":"monitoring"},{"id":36,"name":"networking"},{"id":7,"name":"node-version"},{"id":55,"name":"nosql"},{"id":57,"name":"observability"},{"id":46,"name":"orm"},{"id":52,"name":"os"},{"id":14,"name":"parser"},{"id":74,"name":"react"},{"id":82,"name":"real-time"},{"id":56,"name":"robot"},{"id":65,"name":"runtime"},{"id":32,"name":"sdk"},{"id":71,"name":"search"},{"id":63,"name":"secrets"},{"id":25,"name":"security"},{"id":85,"name":"server"},{"id":86,"name":"serverless"},{"id":70,"name":"storage"},{"id":75,"name":"system-design"},{"id":79,"name":"terminal"},{"id":29,"name":"testing"},{"id":12,"name":"ui"},{"id":50,"name":"ux"},{"id":88,"name":"video"},{"id":20,"name":"web-app"},{"id":35,"name":"web-server"},{"id":43,"name":"webassembly"},{"id":69,"name":"workflow"},{"id":87,"name":"yaml"}]" returns me the "expected json"