{"id":15003676,"url":"https://github.com/luainkernel/lunatik","last_synced_at":"2025-05-15T12:06:11.468Z","repository":{"id":50086084,"uuid":"131418962","full_name":"luainkernel/lunatik","owner":"luainkernel","description":"Lunatik is a framework for scripting the Linux kernel with Lua.","archived":false,"fork":false,"pushed_at":"2025-05-15T00:20:16.000Z","size":5787,"stargazers_count":539,"open_issues_count":42,"forks_count":34,"subscribers_count":16,"default_branch":"master","last_synced_at":"2025-05-15T01:27:35.928Z","etag":null,"topics":["c","kernel","linux-kernel","lua"],"latest_commit_sha":null,"homepage":"","language":"C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/luainkernel.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE-GPL","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2018-04-28T14:58:30.000Z","updated_at":"2025-05-13T15:42:59.000Z","dependencies_parsed_at":"2023-11-15T19:29:01.578Z","dependency_job_id":"b8476f3d-f0a8-454d-9e34-7f27a50cf148","html_url":"https://github.com/luainkernel/lunatik","commit_stats":{"total_commits":4955,"total_committers":30,"mean_commits":"165.16666666666666","dds":0.08113017154389501,"last_synced_commit":"0fb6d5996b267344c04d9c2d5047a5aa3f4fc6cc"},"previous_names":[],"tags_count":17,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/luainkernel%2Flunatik","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/luainkernel%2Flunatik/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/luainkernel%2Flunatik/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/luainkernel%2Flunatik/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/luainkernel","download_url":"https://codeload.github.com/luainkernel/lunatik/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254337613,"owners_count":22054253,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["c","kernel","linux-kernel","lua"],"created_at":"2024-09-24T19:00:11.022Z","updated_at":"2025-05-15T12:06:06.451Z","avatar_url":"https://github.com/luainkernel.png","language":"C","readme":"# Lunatik\n\nLunatik is a framework for scripting the Linux kernel with [Lua](https://www.lua.org/).\nIt is composed by the Lua interpreter modified to run in the kernel;\na [device driver](driver.lua) (written in Lua =)) and a [command line tool](bin/lunatik)\nto load and run scripts and manage runtime environments from the user space;\na [C API](#lunatik-c-api) to load and run scripts and manage runtime environments from the kernel;\nand [Lua APIs](#lunatik-lua-apis) for binding kernel facilities to Lua scripts.\n\nHere is an example of a character device driver written in Lua using Lunatik\nto generate random ASCII printable characters:\n```Lua\n-- /lib/modules/lua/passwd.lua\n--\n-- implements /dev/passwd for generate passwords\n-- usage: $ sudo lunatik run passwd\n--        $ head -c \u003cwidth\u003e /dev/passwd\n\nlocal device = require(\"device\")\nlocal linux  = require(\"linux\")\n\nlocal function nop() end -- do nothing\n\nlocal s = linux.stat\nlocal driver = {name = \"passwd\", open = nop, release = nop, mode = s.IRUGO}\n\nfunction driver:read() -- read(2) callback\n\t-- generate random ASCII printable characters\n\treturn string.char(linux.random(32, 126))\nend\n\n-- creates a new character device\ndevice.new(driver)\n```\n\n## Setup\n\nInstall dependencies (here for Debian/Ubuntu, to be adapted to one's distribution):\n\n```sh\nsudo 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\n```\n\nInstall dependencies (here for Arch Linux):\n\n```sh\nsudo pacman -S git lua clang llvm m4 libpcap pkg-config build2 linux-tools linux-headers\n```\n\nCompile and install `lunatik`:\n\n```sh\nLUNATIK_DIR=~/lunatik  # to be adapted\nmkdir \"${LUNATIK_DIR}\" ; cd \"${LUNATIK_DIR}\"\ngit clone --depth 1 --recurse-submodules https://github.com/luainkernel/lunatik.git\ncd lunatik\nmake\nsudo make install\n```\n\nOnce done, the `debian_kernel_postinst_lunatik.sh` script from tools/ may be copied into\n`/etc/kernel/postinst.d/`: this ensures `lunatik` (and also the `xdp` needed libs) will get\ncompiled on kernel upgrade.\n\n### OpenWRT\n\nInstall Lunatik from our [package feed](https://github.com/luainkernel/openwrt_feed/tree/openwrt-23.05).\n\n## Usage\n\n```\nsudo lunatik # execute Lunatik REPL\nLunatik 3.6  Copyright (C) 2023-2025 ring-0 Ltda.\n\u003e return 42 -- execute this line in the kernel\n42\n```\n\n### lunatik\n\n```Shell\nusage: lunatik [load|unload|reload|status|list] [run|spawn|stop \u003cscript\u003e]\n```\n\n* `load`: load Lunatik kernel modules\n* `unload`: unload Lunatik kernel modules\n* `reload`: reload Lunatik kernel modules\n* `status`: show which Lunatik kernel modules are currently loaded\n* `list`: show which runtime environments are currently running\n* `run`: create a new runtime environment to run the script `/lib/modules/lua/\u003cscript\u003e.lua`\n* `spawn`: create a new runtime environment and spawn a thread to run the script `/lib/modules/lua/\u003cscript\u003e.lua`\n* `stop`: stop the runtime environment created to run the script `\u003cscript\u003e`\n* `default`: start a _REPL (Read–Eval–Print Loop)_\n\n## Lua Version\n\nLunatik 3.6 is based on\n[Lua 5.4 adapted](https://github.com/luainkernel/lua)\nto run in the kernel.\n\n### Floating-point numbers\n\nLunatik **does not** support floating-point arithmetic,\nthus it **does not** support `__div` nor `__pow`\n[metamethods](https://www.lua.org/manual/5.4/manual.html#2.4)\nand the type _number_ has only the subtype _integer_.\n\n### Lua API\n\nLunatik **does not** support both [io](https://www.lua.org/manual/5.4/manual.html#6.8) and\n[os](https://www.lua.org/manual/5.4/manual.html#6.9) libraries,\nand the given identifiers from the following libraries:\n* [debug.debug](https://www.lua.org/manual/5.4/manual.html#pdf-debug.debug),\n[math.acos](https://www.lua.org/manual/5.4/manual.html#pdf-math.acos),\n[math.asin](https://www.lua.org/manual/5.4/manual.html#pdf-math.asin),\n[math.atan](https://www.lua.org/manual/5.4/manual.html#pdf-math.atan),\n[math.ceil](https://www.lua.org/manual/5.4/manual.html#pdf-math.ceil),\n[math.cos](https://www.lua.org/manual/5.4/manual.html#pdf-math.cos),\n[math.deg](https://www.lua.org/manual/5.4/manual.html#pdf-math.deg),\n[math.exp](https://www.lua.org/manual/5.4/manual.html#pdf-math.exp),\n[math.floor](https://www.lua.org/manual/5.4/manual.html#pdf-math.floor),\n[math.fmod](https://www.lua.org/manual/5.4/manual.html#pdf-math.fmod),\n[math.huge](https://www.lua.org/manual/5.4/manual.html#pdf-math.huge).\n[math.log](https://www.lua.org/manual/5.4/manual.html#pdf-math.log),\n[math.modf](https://www.lua.org/manual/5.4/manual.html#pdf-math.modf),\n[math.pi](https://www.lua.org/manual/5.4/manual.html#pdf-math.pi),\n[math.rad](https://www.lua.org/manual/5.4/manual.html#pdf-math.rad),\n[math.random](https://www.lua.org/manual/5.4/manual.html#pdf-math.random),\n[math.randomseed](https://www.lua.org/manual/5.4/manual.html#pdf-math.randomseed),\n[math.sin](https://www.lua.org/manual/5.4/manual.html#pdf-math.sin),\n[math.sqrt](https://www.lua.org/manual/5.4/manual.html#pdf-math.sqrt),\n[math.tan](https://www.lua.org/manual/5.4/manual.html#pdf-math.tan),\n[math.type](https://www.lua.org/manual/5.4/manual.html#pdf-math.type),\n[package.cpath](https://www.lua.org/manual/5.4/manual.html#pdf-package.cpath).\n\nLunatik **modifies** the following identifiers:\n* [\\_VERSION](https://www.lua.org/manual/5.4/manual.html#pdf-_VERSION): is defined as `\"Lua 5.4-kernel\"`.\n* [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_.\n* [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\"`.\n* [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.\n\n### C API\n\nLunatik **does not** support\n[luaL\\_Stream](https://www.lua.org/manual/5.4/manual.html#luaL_Stream),\n[luaL\\_execresult](https://www.lua.org/manual/5.4/manual.html#luaL_execresult),\n[luaL\\_fileresult](https://www.lua.org/manual/5.4/manual.html#luaL_fileresult),\n[luaopen\\_io](https://www.lua.org/manual/5.4/manual.html#pdf-luaopen_io) and\n[luaopen\\_os](https://www.lua.org/manual/5.4/manual.html#pdf-luaopen_os).\n\nLunatik **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).\n\n## Lunatik C API\n\n```C\n#include \u003clunatik.h\u003e\n```\n\n#### lunatik\\_runtime\n```C\nint lunatik_runtime(lunatik_object_t **pruntime, const char *script, bool sleep);\n```\n_lunatik\\_runtime()_ creates a new `runtime` environment then loads and runs the script\n`/lib/modules/lua/\u003cscript\u003e.lua` as the entry point for this environment.\nIt _must_ only be called from _process context_.\nThe `runtime` environment is a Lunatik object that holds\na [Lua state](https://www.lua.org/manual/5.4/manual.html#lua_State).\nLunatik objects are special\nLua [userdata](https://www.lua.org/manual/5.4/manual.html#2.1)\nwhich also hold\na [lock type](https://docs.kernel.org/locking/locktypes.html) and\na [reference counter](https://www.kernel.org/doc/Documentation/kref.txt).\nIf `sleep` is _true_, _lunatik\\_runtime()_ will use a\n[mutex](https://docs.kernel.org/locking/mutex-design.html)\nfor locking the `runtime` environment and the\n[GFP\\_KERNEL](https://www.kernel.org/doc/html/latest/core-api/memory-allocation.html)\nflag for allocating new memory later on on\n[lunatik\\_run()](https://github.com/luainkernel/lunatik#lunatik_run) calls.\nOtherwise, 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).\n_lunatik\\_runtime()_ opens the Lua standard libraries\n[present on Lunatik](https://github.com/luainkernel/lunatik#c-api).\nIf successful, _lunatik\\_runtime()_ sets the address pointed by `pruntime` and\n[Lua's extra space](https://www.lua.org/manual/5.4/manual.html#lua_getextraspace)\nwith a pointer for the new created `runtime` environment,\nsets the _reference counter_ to `1` and then returns `0`.\nOtherwise, it returns `-ENOMEM`, if insufficient memory is available;\nor `-EINVAL`, if it fails to load or run the `script`.\n\n##### Example\n```Lua\n-- /lib/modules/lua/mydevice.lua\nfunction myread(len, off)\n\treturn \"42\"\nend\n```\n\n```C\nstatic lunatik_object_t *runtime;\n\nstatic int __init mydevice_init(void)\n{\n\treturn lunatik_runtime(\u0026runtime, \"mydevice\", true);\n}\n\n```\n\n#### lunatik\\_stop\n```C\nint lunatik_stop(lunatik_object_t *runtime);\n```\n_lunatik\\_stop()_\n[closes](https://www.lua.org/manual/5.4/manual.html#lua_close)\nthe\n[Lua state](https://www.lua.org/manual/5.4/manual.html#lua_State)\ncreated for this `runtime` environment and decrements the\n[reference counter](https://www.kernel.org/doc/Documentation/kref.txt).\nOnce the reference counter is decremented to zero, the\n[lock type](https://docs.kernel.org/locking/locktypes.html)\nand the memory allocated for the `runtime` environment are released.\nIf the `runtime` environment has been released, it returns `1`;\notherwise, it returns `0`.\n\n#### lunatik\\_run\n```C\nvoid lunatik_run(lunatik_object_t *runtime, \u003cinttype\u003e (*handler)(...), \u003cinttype\u003e \u0026ret, ...);\n```\n_lunatik\\_run()_ locks the `runtime` environment and calls the `handler`\npassing the associated Lua state as the first argument followed by the variadic arguments.\nIf the Lua state has been closed, `ret` is set with `-ENXIO`;\notherwise, `ret` is set with the result of `handler(L, ...)` call.\nThen, it restores the Lua stack and unlocks the `runtime` environment.\nIt is defined as a macro.\n\n##### Example\n```C\nstatic int l_read(lua_State *L, char *buf, size_t len, loff_t *off)\n{\n\tsize_t llen;\n\tconst char *lbuf;\n\n\tlua_getglobal(L, \"myread\");\n\tlua_pushinteger(L, len);\n\tlua_pushinteger(L, *off);\n\tif (lua_pcall(L, 2, 2, 0) != LUA_OK) { /* calls myread(len, off) */\n\t\tpr_err(\"%s\\n\", lua_tostring(L, -1));\n\t\treturn -ECANCELED;\n\t}\n\n\tlbuf = lua_tolstring(L, -2, \u0026llen);\n\tllen = min(len, llen);\n\tif (copy_to_user(buf, lbuf, llen) != 0)\n\t\treturn -EFAULT;\n\n\t*off = (loff_t)luaL_optinteger(L, -1, *off + llen);\n\treturn (ssize_t)llen;\n}\n\nstatic ssize_t mydevice_read(struct file *f, char *buf, size_t len, loff_t *off)\n{\n\tssize_t ret;\n\tlunatik_object_t *runtime = (lunatik_object_t *)f-\u003eprivate_data;\n\n\tlunatik_run(runtime, l_read, ret, buf, len, off);\n\treturn ret;\n}\n```\n\n#### lunatik\\_getobject\n```C\nvoid lunatik_getobject(lunatik_object_t *object);\n```\n_lunatik\\_getobject()_ increments the\n[reference counter](https://www.kernel.org/doc/Documentation/kref.txt)\nof this `object` (e.g., `runtime` environment).\n\n#### lunatik\\_put\n```C\nint lunatik_putobject(lunatik_object_t *object);\n```\n_lunatik\\_putobject()_ decrements the\n[reference counter](https://www.kernel.org/doc/Documentation/kref.txt)\nof this `object` (e.g., `runtime` environment).\nIf the `object` has been released, it returns `1`;\notherwise, it returns `0`.\n\n#### lunatik\\_toruntime\n```C\nlunatik_object_t *lunatik_toruntime(lua_State *L);\n```\n_lunatik\\_toruntime()_ returns the `runtime` environment referenced by the `L`'s\n[extra space](https://www.lua.org/manual/5.4/manual.html#lua_getextraspace).\n\n## Lunatik Lua APIs\n\n### lunatik\n\nThe `lunatik` library provides support to load and run scripts and manage runtime environments from Lua.\n\n#### `lunatik.runtime(script [, sleep])`\n\n_lunatik.runtime()_ creates a new\n[runtime environment](https://github.com/luainkernel/lunatik#lunatik_runtime)\nthen loads and runs the script\n`/lib/modules/lua/\u003cscript\u003e.lua` as the entry point for this environment.\nIt returns a Lunatik object representing the `runtime` environment.\nIf `sleep` is _true_ or omitted, it will use a [mutex](https://docs.kernel.org/locking/mutex-design.html)\nand\n[GFP\\_KERNEL](https://www.kernel.org/doc/html/latest/core-api/memory-allocation.html);\notherwise, 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).\n_lunatik.runtime()_ opens the Lua standard libraries\n[present on Lunatik](https://github.com/luainkernel/lunatik#c-api).\n\n#### `runtime:stop()`\n\n_runtime:stop()_\n[stops](https://github.com/luainkernel/lunatik#lunatik_stop)\nthe `runtime` environment and clear its reference from the runtime object.\n\n#### `runtime:resume([obj1, ...])`\n\n_runtime:resume()_\nresumes the execution of a `runtime`.\nThe values `obj1, ...` are passed as the arguments to the function returned on the `runtime` creation.\nIf the `runtime` has yielded, `resume()` restarts it; the values `obj1, ...` are passed as the results from the yield.\n\n### device\n\nThe `device` library provides support for writting\n[character device drivers](https://static.lwn.net/images/pdf/LDD3/ch03.pdf)\nin Lua.\n\n#### `device.new(driver)`\n\n_device.new()_ returns a new `device` object\nand installs its `driver` in the system.\nThe `driver` **must** be defined as a table containing the following field:\n* `name`: string defining the device name; it is used for creating the device file (e.g., `/dev/\u003cname\u003e`).\n\nThe `driver` table might optionally contain the following fields:\n* `read`: callback function to handle the\n[read operation](https://docs.kernel.org/filesystems/vfs.html#id2)\non the device file.\nIt receives the `driver` table as the first argument\nfollowed by two integers,\nthe `length` to be read and the file `offset`.\nIt should return a string and, optionally, the `updated offset`.\nIf the length of the returned string is greater than the requested `length`,\nthe string will be corrected to that `length`.\nIf the `updated offset` is not returned, the `offset` will be updated with `offset + length`.\n* `write`: callback function to handle the\n[write operation](https://docs.kernel.org/filesystems/vfs.html#id2)\non the device file.\nIt receives the `driver` table as the first argument\nfollowed by the string to be written and\nan integer as the file `offset`.\nIt might return optionally the written `length` followed by the `updated offset`.\nIf the returned length is greater than the requested `length`,\nthe returned length will be corrected.\nIf the `updated offset` is not returned, the `offset` will be updated with `offset + length`.\n* `open`: callback function to handle the\n[open operation](https://docs.kernel.org/filesystems/vfs.html#id2)\non the device file.\nIt receives the `driver` table and it is expected to return nothing.\n* `release`: callback function to handle the\n[release operation](https://docs.kernel.org/filesystems/vfs.html#id2)\non the device file.\nIt receives the `driver` table and it is expected to return nothing.\n* `mode`: an integer specifying the device\n[file mode](https://github.com/luainkernel/lunatik#linuxstat).\n\nIf an operation callback is not defined, the `device` returns `-ENXIO` to VFS on its access.\n\n#### `device.stop(dev)`, `dev:stop()`\n\n_device.stop()_ removes a device `driver` specified by the `dev` object from the system.\n\n### linux\n\nThe `linux` library provides support for some Linux kernel facilities.\n\n#### `linux.random([m [, n]])`\n\n_linux.random()_ mimics the behavior of\n[math.random](https://www.lua.org/manual/5.4/manual.html#pdf-math.random),\nbut binding _\u003clinux/random.h\u003e_'s\n[get\\_random\\_u32()](https://elixir.bootlin.com/linux/latest/source/include/linux/random.h#L42)\nand\n[get\\_random\\_u64()](https://elixir.bootlin.com/linux/latest/source/include/linux/random.h#L43)\nAPIs.\n\nWhen called without arguments,\nproduces an integer with all bits (pseudo)random.\nWhen called with two integers `m` and `n`,\n_linux.random()_ returns a pseudo-random integer with uniform distribution in the range `[m, n]`.\nThe call `math.random(n)`, for a positive `n`, is equivalent to `math.random(1, n)`.\n\n#### `linux.stat`\n\n_linux.stat_ is a table that exports\n[\\\u003clinux/stat.h\\\u003e](https://elixir.bootlin.com/linux/latest/source/include/linux/stat.h)\ninteger flags to Lua.\n\n* `\"IRWXUGO\"`: permission to _read_, _write_ and _execute_ for _user_, _group_ and _other_.\n* `\"IRUGO\"`: permission only to _read_ for _user_, _group_ and _other_.\n* `\"IWUGO\"`: permission only to _write_ for _user_, _group_ and _other_.\n* `\"IXUGO\"`: permission only to _execute_ for _user_, _group_ and _other_.\n\n#### `linux.schedule([timeout [, state]])`\n\n_linux.schedule()_ sets the current task `state` and makes the it sleep until `timeout` milliseconds have elapsed.\nIf `timeout` is omitted, it uses `MAX_SCHEDULE_TIMEOUT`.\nIf `state` is omitted, it uses `task.INTERRUPTIBLE`.\n\n#### `linux.task`\n\n_linux.task_ is a table that exports\n[task state](https://elixir.bootlin.com/linux/latest/source/include/linux/sched.h#L7v3)\nflags to Lua.\n\n* `\"RUNNING\"`: task is executing on a CPU or waiting to be executed.\n* `\"INTERRUPTIBLE\"`: task is waiting for a signal or a resource (sleeping).\n* `\"UNINTERRUPTIBLE\"`: behaves like \"INTERRUPTIBLE\" with the exception that signal will not wake up the task.\n* `\"KILLABLE\"`: behaves like \"UNINTERRUPTIBLE\" with the exception that fatal signals will wake up the task.\n* `\"IDLE\"`: behaves like \"UNINTERRUPTIBLE\" with the exception that it avoids the loadavg accounting.\n\n#### `linux.time()`\n\n_linux.time()_ returns the current time in nanoseconds since epoch.\n\n#### `linux.errno`\n\n_linux.errno_ is a table that exports\n[\\\u003cuapi/asm-generic/errno-base.h\\\u003e](https://elixir.bootlin.com/linux/latest/source/include/uapi/asm-generic/errno-base.h)\nflags to Lua.\n\n* `\"PERM\"`: Operation not permitted.\n* `\"NOENT\"`: No such file or directory.\n* `\"SRCH\"`: No such process.\n* `\"INTR\"`: Interrupted system call.\n* `\"IO\"`: I/O error.\n* `\"NXIO\"`:No such device or address.\n* `\"2BIG\"`:, Argument list too long.\n* `\"NOEXEC\"`: Exec format error.\n* `\"BADF\"`: Bad file number.\n* `\"CHILD\"`: No child processes.\n* `\"AGAIN\"`: Try again.\n* `\"NOMEM\"`: Out of memory.\n* `\"ACCES\"`: Permission denied.\n* `\"FAULT\"`: Bad address.\n* `\"NOTBLK\"`: Block device required.\n* `\"BUSY\"`: Device or resource busy.\n* `\"EXIST\"`: File exists.\n* `\"XDEV\"`: Cross-device link.\n* `\"NODEV\"`: No such device.\n* `\"NOTDIR\"`: Not a directory.\n* `\"ISDIR\"`: Is a directory.\n* `\"INVAL\"`: Invalid argument.\n* `\"NFILE\"`: File table overflow.\n* `\"MFILE\"`: Too many open files.\n* `\"NOTTY\"`: Not a typewriter.\n* `\"TXTBSY\"`: Text file busy.\n* `\"FBIG\"`: File too large.\n* `\"NOSPC\"`: No space left on device.\n* `\"SPIPE\"`: Illegal seek.\n* `\"ROFS\"`: Read-only file system.\n* `\"MLINK\"`: Too many links.\n* `\"PIPE\"`: Broken pipe.\n* `\"DOM\"`: Math argument out of domain of func.\n* `\"RANGE\"`: Math result not representable.\n\n#### `linux.hton16(num)`\n\n_linux.hton16()_ converts the host byte order to network byte order for a 16-bit integer.\n\n#### `linux.hton32(num)`\n\n_linux.hton32()_ converts the host byte order to network byte order for a 32-bit integer.\n\n#### `linux.hton64(num)`\n\n_linux.hton64()_ converts the host byte order to network byte order for a 64-bit integer.\n\n#### `linux.ntoh16(num)`\n\n_linux.ntoh16()_ converts the network byte order to host byte order for a 16-bit integer.\n\n#### `linux.ntoh32(num)`\n\n_linux.ntoh32()_ converts the network byte order to host byte order for a 32-bit integer.\n\n#### `linux.ntoh64(num)`\n\n_linux.ntoh64()_ converts the network byte order to host byte order for a 64-bit integer.\n\n#### `linux.htobe16(num)`\n\n_linux.htobe16()_ converts the host byte order to big-endian byte order for a 16-bit integer.\n\n#### `linux.htobe32(num)`\n\n_linux.htobe32()_ converts the host byte order to big-endian byte order for a 32-bit integer.\n\n#### `linux.htobe64(num)`\n\n_linux.htobe64()_ converts the host byte order to big-endian byte order for a 64-bit integer.\n\n#### `linux.be16toh(num)`\n\n_linux.be16toh()_ converts the big-endian byte order to host byte order for a 16-bit integer.\n\n#### `linux.be32toh(num)`\n\n_linux.be32toh()_ converts the big-endian byte order to host byte order for a 32-bit integer.\n\n#### `linux.be64toh(num)`\n\n_linux.be64toh()_ converts the big-endian byte order to host byte order for a 64-bit integer.\n\n#### `linux.htole16(num)`\n\n_linux.htole16()_ converts the host byte order to little-endian byte order for a 16-bit integer.\n\n#### `linux.htole32(num)`\n\n_linux.htole32()_ converts the host byte order to little-endian byte order for a 32-bit integer.\n\n#### `linux.htole64(num)`\n\n_linux.htole64()_ converts the host byte order to little-endian byte order for a 64-bit integer.\n\n#### `linux.le16toh(num)`\n\n_linux.le16toh()_ converts the little-endian byte order to host byte order for a 16-bit integer.\n\n#### `linux.le32toh(num)`\n\n_linux.le32toh()_ converts the little-endian byte order to host byte order for a 32-bit integer.\n\n#### `linux.le64toh(num)`\n\n_linux.le64toh()_ converts the little-endian byte order to host byte order for a 64-bit integer.\n\n### notifier\n\nThe `notifier` library provides support for the kernel\n[notifier chains](https://elixir.bootlin.com/linux/latest/source/include/linux/notifier.h).\n\n#### `notifier.keyboard(callback)`\n\n_notifier.keyboard()_ returns a new keyboard `notifier` object and installs it in the system.\nThe `callback` function is called whenever a console keyboard event happens\n(e.g., a key has been pressed or released).\nThis `callback` receives the following arguments:\n* `event`: the available _events_ are defined by the\n[notifier.kbd](https://github.com/luainkernel/lunatik#notifierkbd) table.\n* `down`: `true`, if the key is pressed; `false`, if it is released.\n* `shift`: `true`, if the shift key is held; `false`, otherwise.\n* `key`: _keycode_ or _keysym_ depending on `event`.\n\nThe `callback` function might return the values defined by the\n[notifier.notify](https://github.com/luainkernel/lunatik#notifiernotify) table.\n\n#### `notifier.kbd`\n\n_notifier.kbd_ is a table that exports\n[KBD](https://elixir.bootlin.com/linux/latest/source/include/linux/notifier.h#L229)\nflags to Lua.\n\n* `\"KEYCODE\"`: keyboard _keycode_, called before any other.\n* `\"UNBOUND_KEYCODE\"`: keyboard _keycode_ which is not bound to any other.\n* `\"UNICODE\"`: keyboard unicode.\n* `\"KEYSYM\"`: keyboard _keysym_.\n* `\"POST_KEYSYM\"`: called after keyboard _keysym_ interpretation.\n\n#### `notifier.netdevice(callback)`\n\n_notifier.netdevice()_ returns a new netdevice `notifier` object and installs it in the system.\nThe `callback` function is called whenever a console netdevice event happens\n(e.g., a network interface has been connected or disconnected).\nThis `callback` receives the following arguments:\n* `event`: the available _events_ are defined by the\n[notifier.netdev](https://github.com/luainkernel/lunatik#notifiernetdev) table.\n* `name`: the device name.\n\nThe `callback` function might return the values defined by the\n[notifier.notify](https://github.com/luainkernel/lunatik#notifiernotify) table.\n\n#### `notifier.netdev`\n\n_notifier.netdev_ is a table that exports\n[NETDEV](https://elixir.bootlin.com/linux/v6.3/source/include/linux/netdevice.h#L2812)\nflags to Lua.\n\n#### `notifier.notify`\n\n_notifier.notify_ is a table that exports\n[NOTIFY](https://elixir.bootlin.com/linux/latest/source/include/linux/notifier.h#L183)\nflags to Lua.\n\n* `\"DONE\"`: don't care.\n* `\"OK\"`: suits me.\n* `\"BAD\"`: bad/veto action.\n* `\"STOP\"`: clean way to return from the notifier and stop further calls.\n\n#### `notfr:delete()`\n\n_notfr:delete()_ removes a `notifier` specified by the `notfr` object from the system.\n\n### socket\n\nThe `socket` library provides support for the kernel\n[networking handling](https://elixir.bootlin.com/linux/latest/source/include/linux/net.h).\nThis library was inspired by\n[Chengzhi Tan](https://github.com/tcz717)'s\n[GSoC project](https://summerofcode.withgoogle.com/archive/2018/projects/5993341447569408).\n\n#### `socket.new(family, type, protocol)`\n\n_socket.new()_ creates a new `socket` object.\nThis function receives the following arguments:\n* `family`: the available _address families_ are defined by the\n[socket.af](https://github.com/luainkernel/lunatik#socketaf) table.\n* `sock`: the available _types_ are present on the\n[socket.sock](https://github.com/luainkernel/lunatik#socketsock) table.\n* `protocol`: the available _protocols_ are defined by the\n[socket.ipproto](https://github.com/luainkernel/lunatik#socketipproto) table.\n\n#### `socket.af`\n\n_socket.af_ is a table that exports\n[address families (AF)](https://elixir.bootlin.com/linux/latest/source/include/linux/socket.h#L187)\nto Lua.\n\n* `\"UNSPEC\"`: Unspecified.\n* `\"UNIX\"`: Unix domain sockets.\n* `\"LOCAL\"`: POSIX name for AF\\_UNIX.\n* `\"INET\"`: Internet IP Protocol.\n* `\"AX25\"`: Amateur Radio AX.25.\n* `\"IPX\"`: Novell IPX.\n* `\"APPLETALK\"`: AppleTalk DDP.\n* `\"NETROM\"`: Amateur Radio NET/ROM.\n* `\"BRIDGE\"`: Multiprotocol bridge.\n* `\"ATMPVC\"`: ATM PVCs.\n* `\"X25\"`: Reserved for X.25 project.\n* `\"INET6\"`: IP version 6.\n* `\"ROSE\"`: Amateur Radio X.25 PLP.\n* `\"DEC\"`: Reserved for DECnet project.\n* `\"NETBEUI\"`: Reserved for 802.2LLC project.\n* `\"SECURITY\"`: Security callback pseudo AF.\n* `\"KEY\"`: PF\\_KEY key management API.\n* `\"NETLINK\"`: Netlink.\n* `\"ROUTE\"`: Alias to emulate 4.4BSD.\n* `\"PACKET\"`: Packet family.\n* `\"ASH\"`: Ash.\n* `\"ECONET\"`: Acorn Econet.\n* `\"ATMSVC\"`: ATM SVCs.\n* `\"RDS\"`: RDS sockets.\n* `\"SNA\"`: Linux SNA Project (nutters!).\n* `\"IRDA\"`: IRDA sockets.\n* `\"PPPOX\"`: PPPoX sockets.\n* `\"WANPIPE\"`: Wanpipe API Sockets.\n* `\"LLC\"`: Linux LLC.\n* `\"IB\"`: Native InfiniBand address.\n* `\"MPLS\"`: MPLS.\n* `\"CAN\"`: Controller Area Network.\n* `\"TIPC\"`: TIPC sockets.\n* `\"BLUETOOTH\"`: Bluetooth sockets.\n* `\"IUCV\"`: IUCV sockets.\n* `\"RXRPC\"`: RxRPC sockets.\n* `\"ISDN\"`: mISDN sockets.\n* `\"PHONET\"`: Phonet sockets.\n* `\"IEEE802154\"`: IEEE802154 sockets.\n* `\"CAIF\"`: CAIF sockets.\n* `\"ALG\"`: Algorithm sockets.\n* `\"NFC\"`: NFC sockets.\n* `\"VSOCK\"`: vSockets.\n* `\"KCM\"`: Kernel Connection Multiplexor.\n* `\"QIPCRTR\"`: Qualcomm IPC Router.\n* `\"SMC\"`: reserve number for PF\\_SMC protocol family that reuses AF\\_INET address family.\n* `\"XDP\"`: XDP sockets.\n* `\"MCTP\"`: Management component transport protocol.\n* `\"MAX\"`: Maximum.\n\n#### `socket.sock`\n\n_socket.sock_ is a table that exports socket\n[types (SOCK)](https://elixir.bootlin.com/linux/latest/source/include/linux/net.h#L49):\n\n* `\"STREAM\"`: stream (connection) socket.\n* `\"DGRAM\"`: datagram (conn.less) socket.\n* `\"RAW\"`: raw socket.\n* `\"RDM\"`: reliably-delivered message.\n* `\"SEQPACKET\"`: sequential packet socket.\n* `\"DCCP\"`: Datagram Congestion Control Protocol socket.\n* `\"PACKET\"`: linux specific way of getting packets at the dev level.\n\nand [flags (SOCK)](https://elixir.bootlin.com/linux/latest/source/include/linux/net.h#L78):\n* `\"CLOEXEC\"`: n/a.\n* `\"NONBLOCK\"`: n/a.\n\n#### `socket.ipproto`\n\n_socket.ipproto_ is a table that exports\n[IP protocols (IPPROTO)](https://elixir.bootlin.com/linux/latest/source/include/uapi/linux/in.h#L27)\nto Lua.\n\n* `\"IP\"`: Dummy protocol for TCP.\n* `\"ICMP\"`: Internet Control Message Protocol.\n* `\"IGMP\"`: Internet Group Management Protocol.\n* `\"IPIP\"`: IPIP tunnels (older KA9Q tunnels use 94).\n* `\"TCP\"`: Transmission Control Protocol.\n* `\"EGP\"`: Exterior Gateway Protocol.\n* `\"PUP\"`: PUP protocol.\n* `\"UDP\"`: User Datagram Protocol.\n* `\"IDP\"`: XNS IDP protocol.\n* `\"TP\"`: SO Transport Protocol Class 4.\n* `\"DCCP\"`: Datagram Congestion Control Protocol.\n* `\"IPV6\"`: IPv6-in-IPv4 tunnelling.\n* `\"RSVP\"`: RSVP Protocol.\n* `\"GRE\"`: Cisco GRE tunnels (rfc 1701,1702).\n* `\"ESP\"`: Encapsulation Security Payload protocol.\n* `\"AH\"`: Authentication Header protocol.\n* `\"MTP\"`: Multicast Transport Protocol.\n* `\"BEETPH\"`: IP option pseudo header for BEET.\n* `\"ENCAP\"`: Encapsulation Header.\n* `\"PIM\"`: Protocol Independent Multicast.\n* `\"COMP\"`: Compression Header Protocol.\n* `\"SCTP\"`: Stream Control Transport Protocol.\n* `\"UDPLITE\"`: UDP-Lite (RFC 3828).\n* `\"MPLS\"`: MPLS in IP (RFC 4023).\n* `\"ETHERNET\"`: Ethernet-within-IPv6 Encapsulation.\n* `\"RAW\"`: Raw IP packets.\n* `\"MPTCP\"`: Multipath TCP connection.\n\n#### `sock:close()`\n\n_sock:close()_ removes `sock` object from the system.\n\n#### `sock:send(message, [addr [, port]])`\n\n_sock:send()_ sends a string `message` through the socket `sock`.\nIf the `sock` address family is `af.INET`, then it expects the following arguments:\n* `addr`: `integer` describing the destination IPv4 address.\n* `port`: `integer` describing the destination IPv4 port.\n\nOtherwise:\n* `addr`: [packed string](https://www.lua.org/manual/5.4/manual.html#6.4.2) describing the destination address.\n\n#### `sock:receive(length, [flags [, from]])`\n\n_sock:receive()_ receives a string with up to `length` bytes through the socket `sock`.\nThe available _message flags_ are defined by the\n[socket.msg](https://github.com/luainkernel/lunatik#socketmsg) table.\nIf `from` is `true`, it returns the received message followed by the peer's address.\nOtherwise, it returns only the received message.\n\n#### `socket.msg`\n\n_socket.msg_ is a table that exports\n[message flags](https://elixir.bootlin.com/linux/latest/source/include/linux/socket.h#L298)\nto Lua.\n\n* `\"OOB\"`: n/a.\n* `\"PEEK\"`: n/a.\n* `\"DONTROUTE\"`: n/a.\n* `\"TRYHARD\"`: Synonym for `\"DONTROUTE\"` for DECnet.\n* `\"CTRUNC\"`: n/a.\n* `\"PROBE\"`: Do not send. Only probe path f.e. for MTU.\n* `\"TRUNC\"`: n/a.\n* `\"DONTWAIT\"`: Nonblocking io.\n* `\"EOR\"`: End of record.\n* `\"WAITALL\"`: Wait for a full request.\n* `\"FIN\"`: n/a.\n* `\"SYN\"`: n/a.\n* `\"CONFIRM\"`: Confirm path validity.\n* `\"RST\"`: n/a.\n* `\"ERRQUEUE\"`: Fetch message from error queue.\n* `\"NOSIGNAL\"`: Do not generate SIGPIPE.\n* `\"MORE\"`: Sender will send more.\n* `\"WAITFORONE\"`: recvmmsg(): block until 1+ packets avail.\n* `\"SENDPAGE_NOPOLICY\"`: sendpage() internal: do no apply policy.\n* `\"SENDPAGE_NOTLAST\"`: sendpage() internal: not the last page.\n* `\"BATCH\"`: sendmmsg(): more messages coming.\n* `\"EOF\"`: n/a.\n* `\"NO_SHARED_FRAGS\"`: sendpage() internal: page frags are not shared.\n* `\"SENDPAGE_DECRYPTED\"`: sendpage() internal: page may carry plain text and require encryption.\n* `\"ZEROCOPY\"`: Use user data in kernel path.\n* `\"FASTOPEN\"`: Send data in TCP SYN.\n* `\"CMSG_CLOEXEC\"`: Set close\\_on\\_exec for file descriptor received through SCM\\_RIGHTS.\n\n#### `sock:bind(addr [, port])`\n\n_sock:bind()_ binds the socket `sock` to a given address.\nIf the `sock` address family is `af.INET`, then it expects the following arguments:\n* `addr`: `integer` describing host IPv4 address.\n* `port`: `integer` describing host IPv4 port.\n\nOtherwise:\n* `addr`: [packed string](https://www.lua.org/manual/5.4/manual.html#6.4.2) describing host address.\n\n#### `sock:listen([backlog])`\n\n_sock:listen()_ moves the socket `sock` to listening state.\n* `backlog`: pending connections queue size.\nIf omitted, it uses\n[SOMAXCONN](https://elixir.bootlin.com/linux/latest/source/include/linux/socket.h#L296)\nas default.\n\n#### `sock:accept([flags])`\n\n_sock:accept()_ accepts a connection on socket `sock`.\nIt returns a new `socket` object.\nThe available _flags_ are present on the\n[socket.sock](https://github.com/luainkernel/lunatik#socketsock) table.\n\n#### `sock:connect(addr [, port] [, flags])`\n\n_sock:connect()_ connects the socket `sock` to the address `addr`.\nIf the `sock` address family is `af.INET`, then it expects the following arguments:\n* `addr`: `integer` describing the destination IPv4 address.\n* `port`: `integer` describing the destination IPv4 port.\n\nOtherwise:\n* `addr`: [packed string](https://www.lua.org/manual/5.4/manual.html#6.4.2) describing the destination address.\n\nThe available _flags_ are present on the\n[socket.sock](https://github.com/luainkernel/lunatik#socketsock) table.\n\nFor datagram sockets, `addr` is the address to which datagrams are sent\nby default, and the only address from which datagrams are received.\nFor stream sockets, attempts to connect to `addr`.\n\n#### `sock:getsockname()`\n\n_sock:getsockname()_ get the address which the socket `sock` is bound.\nIf the `sock` address family is `af.INET`, then it returns the following:\n* `addr`: `integer` describing the bounded IPv4 address.\n* `port`: `integer` describing the bounded IPv4 port.\n\nOtherwise:\n* `addr`: [packed string](https://www.lua.org/manual/5.4/manual.html#6.4.2) describing the bounded address.\n\n#### `sock:getpeername()`\n\n_sock:getpeername()_ get the address which the socket `sock` is connected.\nIf the `sock` address family is `af.INET`, then it returns the following:\n* `addr`: `integer` describing the peer's IPv4 address.\n* `port`: `integer` describing the peer's IPv4 port.\n\nOtherwise:\n* `addr`: [packed string](https://www.lua.org/manual/5.4/manual.html#6.4.2) describing the peer's address.\n\n### socket.inet\n\nThe `socket.inet` library provides support for high-level IPv4 sockets.\n\n#### `inet.tcp()`\n\n_inet.tcp()_ creates a new `socket` using\n[af.INET](https://github.com/luainkernel/lunatik#socketaf) address family,\n[sock.STREAM](https://github.com/luainkernel/lunatik#socketsock) type\nand\n[ipproto.TCP](https://github.com/luainkernel/lunatik#socketipproto) protocol.\nIt overrides `socket` methods to use addresses as _numbers-and-dots notation_\n(e.g., `\"127.0.0.1\"`), instead of integers.\n\n#### `inet.udp()`\n\n_inet.udp()_ creates a new `socket` using\n[af.INET](https://github.com/luainkernel/lunatik#socketaf) address family,\n[sock.DGRAM](https://github.com/luainkernel/lunatik#socketsock) type\nand\n[ipproto.UDP](https://github.com/luainkernel/lunatik#socketipproto) protocol.\nIt overrides `socket` methods to use addresses as _numbers-and-dots notation_\n(e.g., `\"127.0.0.1\"`), instead of integers.\n\n##### `udp:receivefrom(length [, flags])`\n\n_udp:receivefrom()_ is just an alias to `sock:receive(length, flags, true)`.\n\n### net\n\nThe `net` library provides utility functions for working with IPv4 addresses.\n\n#### `net.aton(addr)`\n\n_net.aton()_ converts an IPv4 address from dotted-decimal notation (e.g., `\"127.0.0.1\"`) to its integer representation.\n\n* `addr`: A string representing the IPv4 address in dotted-decimal notation.\n\nReturns the integer representation of the IPv4 address.\n\n#### `net.ntoa(ip)`\n\n_net.ntoa()_ converts an IPv4 address from its integer representation to dotted-decimal notation (e.g., `\"127.0.0.1\"`).\n\n* `ip`: An integer representing the IPv4 address.\n\nReturns the dotted-decimal string representation of the IPv4 address.\n\n### rcu\n\nThe `rcu` library provides support for the kernel\n[Read-copy update (RCU)](https://lwn.net/Articles/262464/)\nsynchronization mechanism.\nThis library was inspired by\n[Caio Messias](https://github.com/cmessias)'\n[GSoC project](https://summerofcode.withgoogle.com/archive/2018/projects/5736202426646528).\n\n#### `rcu.table([size])`\n\n_rcu.table()_ creates a new `rcu.table` object\nwhich binds the kernel [generic hash table](https://lwn.net/Articles/510202/).\nThis function receives as argument the number of buckets rounded up to the next power of 2.\nThe default size is `1024`.\nKey must be a string and value must be a Lunatik object or nil.\n\n### thread\n\nThe `thread` library provides support for the\n[kernel thread primitives](https://lwn.net/Articles/65178/).\n\n#### `thread.run(runtime, name)`\n\n_thread.run()_ creates a new `thread` object and wakes it up.\nThis function receives the following arguments:\n* `runtime`: the\n[runtime environment](https://github.com/luainkernel/lunatik#lunatikruntimescript--sleep)\nfor running a task in the created kernel thread.\nThe task must be specified by returning a function on the script loaded\nin the `runtime` environment.\n* `name`: string representing the name for the thread (e.g., as shown on `ps`).\n\n#### `thread.shouldstop()`\n\n_thread.shouldstop()_ returns `true` if\n[thread.stop()](https://github.com/luainkernel/lunatik#threadstopthrd-thrdstop)\nwas called; otherwise, it returns `false`.\n\n#### `thread.current()`\n\n_thread.current()_ returns a `thread` object representing the current task.\n\n#### `thrd:stop()`\n\n_thrd:stop()_ sets\n[thread.shouldstop()](https://github.com/luainkernel/lunatik#threadshouldstop)\non the thread `thrd` to return true, wakes `thrd`, and waits for it to exit.\n\n#### `thrd:task()`\n\n_thrd:task()_ returns a table containing the task information of this `thread`\n(e.g., \"cpu\", \"command\", \"pid\" and \"tgid\").\n\n### fib\n\nThe `fib` library provides support for the\n[kernel Forwarding Information Base](https://thermalcircle.de/doku.php?id=blog:linux:routing_decisions_in_the_linux_kernel_1_lookup_packet_flow).\n\n#### `fib.newrule(table, priority)`\n\n_fib.newrule()_ binds the kernel\n[fib_nl_newrule](https://elixir.bootlin.com/linux/latest/source/include/net/fib_rules.h#L182)\nAPI;\nit creates a new FIB rule that matches the specified routing _table_\nwith the specified _priorioty_.\nThis function is similar to the user-space command\n[ip rule add](https://datahacker.blog/industry/technology-menu/networking/iptables/follow-the-ip-rules)\nprovided by [iproute2](https://wiki.linuxfoundation.org/networking/iproute2).\n\n#### `fib.delrule(table, priority)`\n\n_fib.delrule()_ binds the kernel\n[fib_nl_delrule](https://elixir.bootlin.com/linux/latest/source/include/net/fib_rules.h#L184)\nAPI;\nit removes a FIB rule that matches the specified routing _table_\nwith the specified _priorioty_.\nThis function is similar to the user-space command\n[ip rule del](https://datahacker.blog/industry/technology-menu/networking/iptables/follow-the-ip-rules)\nprovided by [iproute2](https://wiki.linuxfoundation.org/networking/iproute2).\n\n### data\n\nThe `data` library provides support for binding the system memory to Lua.\n\n#### `data.new(size)`\n\n_data.new()_ creates a new `data` object which allocates `size` bytes.\n\n#### `d:getnumber(offset)`\n\n_d:getnumber()_ extracts a [lua\\_Integer](https://www.lua.org/manual/5.4/manual.html#lua_Integer)\nfrom the memory referenced by a `data` object and a byte `offset`,\nstarting from zero.\n\n#### `d:setnumber(offset, number)`\n\n_d:setnumber()_ insert a [lua\\_Integer](https://www.lua.org/manual/5.4/manual.html#lua_Integer)\n`number` into the memory referenced by a `data` object and a byte `offset`,\nstarting from zero.\n\n#### `d:getbyte(offset)`\n\n_d:getbyte()_ extracts a byte\nfrom the memory referenced by a `data` object and a byte `offset`,\nstarting from zero.\n\n#### `d:setbyte(offset, byte)`\n\n_d:setbyte()_ insert a byte\ninto the memory referenced by a `data` object and a byte `offset`,\nstarting from zero.\n\n#### `d:getstring(offset[, length])`\n\n_d:getstring()_ extracts a string with `length` bytes\nfrom the memory referenced by a `data` object and a byte `offset`,\nstarting from zero. If `length` is omitted, it extracts all bytes\nfrom `offset` to the end of the `data`.\n\n#### `d:setstring(offset, s)`\n\n_d:setstring()_ insert the string `s`\ninto the memory referenced by a `data` object and a byte `offset`,\nstarting from zero.\n\n#### `d:getint8(offset)`\n\n_d:getint8(d, offset)_ extracts a signed 8-bit integer\nfrom the memory referenced by a `data` object and a byte `offset`,\nstarting from zero.\n\n#### `d:setint8(offset, number)`\n\n_d:setint8()_ inserts a signed 8-bit number\ninto the memory referenced by a `data` object and a byte `offset`,\nstarting from zero.\n\n#### `d:getuint8(offset)`\n\n_d:getuint8()_ extracts an unsigned 8-bit integer\nfrom the memory referenced by a `data` object and a byte `offset`,\nstarting from zero.\n\n#### `d:setuint8(offset, number)`\n\n_d:setuint8()_ inserts an unsigned 8-bit number\ninto the memory referenced by a `data` object and a byte `offset`,\nstarting from zero.\n\n#### `d:getint16(offset)`\n\n_d:getint16()_ extracts a signed 16-bit integer\nfrom the memory referenced by a `data` object and a byte `offset`,\nstarting from zero.\n\n#### `d:setint16(offset, number)`\n\n_d:setint16()_ inserts a signed 16-bit number\ninto the memory referenced by a `data` object and a byte `offset`,\nstarting from zero.\n\n#### `d:getuint16(offset)`\n\n_d:getuint16()_ extracts an unsigned 16-bit integer\nfrom the memory referenced by a `data` object and a byte `offset`,\nstarting from zero.\n\n#### `d:setuint16(offset, number)`\n\n_d:setuint16()_ inserts an unsigned 16-bit number\ninto the memory referenced by a `data` object and a byte `offset`,\nstarting from zero.\n\n#### `d:getint32(offset)`\n\n_d:getint32()_ extracts a signed 32-bit integer\nfrom the memory referenced by a `data` object and a byte `offset`,\nstarting from zero.\n\n#### `d:setint32(offset, number)`\n\n_d:setint32()_ inserts a signed 32-bit number\ninto the memory referenced by a `data` object and a byte `offset`,\nstarting from zero.\n\n#### `d:getuint32(offset)`\n\n_d:getuint32()_ extracts an unsigned 32-bit integer\nfrom the memory referenced by a `data` object and a byte `offset`,\nstarting from zero.\n\n#### `d:setuint32(offset, number)`\n\n_d:setuint32()_ inserts an unsigned 32-bit number\ninto the memory referenced by a `data` object and a byte `offset`,\nstarting from zero.\n\n#### `d:getint64(offset)`\n\n_d:getint64()_ extracts a signed 64-bit integer\nfrom the memory referenced by a `data` object and a byte `offset`,\nstarting from zero.\n\n#### `d:setint64(offset, number)`\n\n_d:setint64()_ inserts a signed 64-bit number\ninto the memory referenced by a `data` object and a byte `offset`,\nstarting from zero.\n\n### probe\n\nThe `probe` library provides support for\n[kernel probes](https://docs.kernel.org/trace/kprobes.html).\n\n#### `probe.new(symbol|address, handlers)`\n\n_probe.new()_ returns a new `probe` object for monitoring a kernel `symbol` (string) or `address` (light userdata)\nand installs its `handlers` in the system.\nThe `handler` **must** be defined as a table containing the following field:\n* `pre`: function to be called before the probed instruction.\nIt receives the `symbol` or `address`,\nfollowed by a closure that may be called to\n[show the CPU registers and stack](https://elixir.bootlin.com/linux/v5.6.19/source/include/linux/sched/debug.h#L26)\nin the system log.\n* `post`: function to be called after the probed instruction.\nIt receives the `symbol` or `address`,\nfollowed by a closure that may be called to\n[show the CPU registers and stack](https://elixir.bootlin.com/linux/v5.6.19/source/include/linux/sched/debug.h#L26)\nin the system log.\n\n#### `p:stop()`\n\n_p:stop()_ removes the `probe` handlers from the system.\n\n#### `p:enable(bool)`\n\n_p:enable()_ enables or disables the `probe` handlers, accordingly to `bool`.\n\n### syscall\n\nThe `syscall` library provides support for system call addresses and numbers.\n\n#### `syscall.address(number)`\n\n_syscall.address()_ returns the system call address (light userdata) referenced by the given `number`.\n\n#### `syscall.number(name)`\n\n_syscall.number()_ returns the system call number referenced by the given `name`.\n\n### syscall.table\n\nThe `syscall.table` library provides support for translating system call names to addresses (light userdata).\n\n### xdp\n\nThe `xdp` library provides support for the kernel\n[eXpress Data Path (XDP)](https://prototype-kernel.readthedocs.io/en/latest/networking/XDP/)\nsubsystem.\nThis library was inspired by\n[Victor Nogueira](https://github.com/VictorNogueiraRio/linux)'s\n[GSoC project](https://victornogueirario.github.io/xdplua/).\n\n#### `xdp.attach(callback)`\n\n_xdp.attach()_ registers a `callback` function to the current `runtime`\nto be called from an XDP/eBPF program whenever it calls\n[bpf_luaxdp_run](lib/luaxdp.c#L106)\n[kfunc](https://docs.kernel.org/bpf/kfuncs.html).\nThis `callback` receives the following arguments:\n* `buffer`: a `data` object representing the network buffer.\n* `argument`: a `data` object containing the argument passed by the XDP/eBPF program.\n\nThe `callback` function might return the values defined by the\n[xdp.action](https://github.com/luainkernel/lunatik#xdpaction) table.\n\n#### `xdp.detach()`\n\n_xdp.detach()_ unregisters the `callback` associated with the current `runtime`, if any.\n\n#### `xdp.action`\n\n_xdp.action_ is a table that exports\n[xdp_action](https://elixir.bootlin.com/linux/v6.4/source/include/uapi/linux/bpf.h#L6187)\nflags to Lua.\n\n* `\"ABORTED\"`: Indicates that the XDP program aborted, typically due to an error.\n* `\"DROP\"`: Specifies that the packet should be dropped, discarding it entirely.\n* `\"PASS\"`: Allows the packet to pass through to the Linux network stack.\n* `\"TX\"`: Transmits the packet back out on the same interface it was received.\n* `\"REDIRECT\"`: Redirects the packet to another interface or processing context.\n\n### xtable\n\nThe `xtable` library provides support for developing netfilter [xtable extensions](https://inai.de/projects/xtables-addons/).\n\n#### `xtable.match(opts)`\n\n_xtable.match()_ returns a new [xtable](https://inai.de/projects/xtables-addons/) object for match extensions.\nThis function receives the following arguments:\n* `opts`: a table containing the following fields:\n  * `name`: string representing the xtable extension name.\n  * `revision`: integer representing the xtable extension revision.\n  * `family`: address family, one of [netfilter.family](https://github.com/luainkernel/lunatik#netfilterfamily).\n  * `proto`: protocol number, one of [socket.ipproto](https://github.com/luainkernel/lunatik#socketipproto).\n  * `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 \u003c\u003c inet_hooks.LOCAL_OUT`).\n  * `match` : function to be called for matching packets. It receives the following arguments:\n\t* `skb` (readonly): a `data` object representing the socket buffer.\n\t* `par`: a table containing `hotdrop`, `thoff` (transport header offset) and `fragoff` (fragment offset) fields.\n    * `userargs` : a lua string passed from the userspace xtable module.\n    * The function must return `true` if the packet matches the extension; otherwise, it must return `false`.\n  * `checkentry`: function to be called for checking the entry. This function receives `userargs` as its argument.\n  * `destroy`: function to be called for destroying the xtable extension. This function receives `userargs` as its argument.\n\n#### `xtable.target(opts)`\n\n_xtable.target()_ returns a new [xtable](https://inai.de/projects/xtables-addons/) object for target extension.\nThis function receives the following arguments:\n* `opts`: a table containing the following fields:\n  * `name`: string representing the xtable extension name.\n  * `revision`: integer representing the xtable extension revision.\n  * `family`: address family, one of [netfilter.family](https://github.com/luainkernel/lunatik#netfilterfamily).\n  * `proto`: protocol number, one of [socket.ipproto](https://github.com/luainkernel/lunatik#socketipproto).\n  * `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 \u003c\u003c inet_hooks.LOCAL_OUT`).\n  * `target` : function to be called for targeting packets. It receives the following arguments:\n    * `skb`: a `data` object representing the socket buffer.\n    * `par` (readonly): a table containing `hotdrop`, `thoff` (transport header offset) and `fragoff` (fragment offset) fields.\n    * `userargs` : a lua string passed from the userspace xtable module.\n    * The function must return one of the values defined by the [netfilter.action](https://github.com/luainkernel/lunatik#netfilteraction) table.\n  * `checkentry`: function to be called for checking the entry. This function receives `userargs` as its argument.\n  * `destroy`: function to be called for destroying the xtable extension. This function receives `userargs` as its argument.\n\n### netfilter\n\nThe `netfilter` library provides support for the [new netfilter hook](https://www.netfilter.org/documentation/HOWTO/netfilter-hacking-HOWTO-4.html#ss4.6) system.\n\n#### `netfilter.register(ops)`\n\n_netfilter.register()_ registers a new netfilter hook with the given `ops` table.\nThis function receives the following arguments:\n* `ops`: a table containing the following fields:\n  * `pf`: protocol family, one of [netfilter.family](https://github.com/luainkernel/lunatik#netfilterfamily)\n  * `hooknum`:\thook 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`).\n  * `priority`:\tpriority 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.\n  * `hook`: function to be called for the hook. It receives the following arguments:\n\t* `skb`: a `data` object representing the socket buffer. The object points to the beginning of the packet. In stardard cases, where the Ethernet header is present, it points to its start. Otherwise, the object points to the start of the IP header (E.g - for hooks in `LOCAL_OUT`).\n\t* The function must return one of the values defined by the [netfilter.action](https://github.com/luainkernel/lunatik#netfilteraction).\n\n#### `netfilter.family`\n\n_netfilter.family_ is a table that exports\naddress families to Lua.\n\n* `\"UNSPEC\"`: Unspecified.\n* `\"INET\"`: Internet Protocol version 4.\n* `\"IPV4\"`: Internet Protocol version 4.\n* `\"IPV6\"`: Internet Protocol version 6.\n* `\"ARP\"`: Address Resolution Protocol.\n* `\"NETDEV\"`: Device ingress and egress path\n* `\"BRIDGE\"`: Ethernet Bridge.\n\n#### `netfilter.action`\n\n_netfilter.action_ is a table that exports\nnetfilter actions to Lua.\n\n* `\"DROP\"`: `NF_DROP`. The packet is dropped. It is not forwarded, processed, or seen by any other network layer.\n* `\"ACCEPT\"`: `NF_ACCEPT`. The packet is accepted and passed to the next step in the network processing chain.\n* `\"STOLEN\"`: `NF_STOLEN`. The packet is taken by the handler, and processing stops.\n* `\"QUEUE\"`: `NF_QUEUE`. The packet is queued for user-space processing.\n* `\"REPEAT\"`: `NF_REPEAT`. The packet is sent through the hook chain again.\n* `\"STOP\"`: `NF_STOP`. Processing of the packet stops.\n* `\"CONTINUE\"`: `XT_CONTINUE`. Return the packet should continue traversing the rules within the same table.\n* `\"RETURN\"`: `XT_RETURN`. Return the packet to the previous chain.\n\n#### `netfilter.inet_hooks`\n\n_netfilter.inet_hooks_ is a table that exports\ninet netfilter hooks to Lua.\n\n* `\"PRE_ROUTING\"`: `NF_INET_PRE_ROUTING`. The packet is received by the network stack.\n* `\"LOCAL_IN\"`: `NF_INET_LOCAL_IN`. The packet is destined for the local system.\n* `\"FORWARD\"`: `NF_INET_FORWARD`. The packet is to be forwarded to another host.\n* `\"LOCAL_OUT\"`: `NF_INET_LOCAL_OUT`. The packet is generated by the local system.\n* `\"POST_ROUTING\"`: `NF_INET_POST_ROUTING`. The packet is about to be sent out.\n\n#### `netfilter.bridge_hooks`\n\n_netfilter.bridge_hooks_ is a table that exports\nbridge netfilter hooks to Lua.\n\n* `\"PRE_ROUTING\"`: `NF_BR_PRE_ROUTING`. First hook invoked, runs before forward database is consulted.\n* `\"LOCAL_IN\"`: `NF_BR_LOCAL_IN`. Invoked for packets destined for the machine where the bridge was configured on.\n* `\"FORWARD\"`: `NF_BR_FORWARD`. Called for frames that are bridged to a different port of the same logical bridge device.\n* `\"LOCAL_OUT\"`: `NF_BR_LOCAL_OUT`. Called for locally originating packets that will be transmitted via the bridge.\n* `\"POST_ROUTING\"`: `NF_BR_POST_ROUTING`. Called for all locally generated packets and all bridged packets\n\n#### `netfilter.arp_hooks`\n\n_netfilter.arp_hooks_ is a table that exports\narp netfilter hooks to Lua.\n\n* `\"IN\"`: `NF_ARP_IN`. The packet is received by the network stack.\n* `\"OUT\"`: `NF_ARP_OUT`. The packet is generated by the local system.\n* `\"FORWARD\"`: `NF_ARP_FORWARD`. The packet is to be forwarded to another host.\n\n#### `netfilter.netdev_hooks`\n\n_netfilter.netdev_hooks_ is a table that exports\nnetdev netfilter hooks to Lua.\n\n* `\"INGRESS\"`: `NF_NETDEV_INGRESS`. The packet is received by the network stack.\n* `\"EGRESS\"`: `NF_NETDEV_EGRESS`. The packet is generated by the local system.\n\n#### `netfilter.ip_priority`\n\n_netfilter.ip_priority_ is a table that exports\nnetfilter IPv4/IPv6 priority levels to Lua.\n\n* `\"FIRST\"`: `NF_IP_PRI_FIRST`\n* `\"RAW_BEFORE_DEFRAG\"`: `NF_IP_PRI_RAW_BEFORE_DEFRAG`\n* `\"CONNTRACK_DEFRAG\"`: `NF_IP_PRI_CONNTRACK_DEFRAG`\n* `\"RAW\"`: `NF_IP_PRI_RAW`\n* `\"SELINUX_FIRST\"`: `NF_IP_PRI_SELINUX_FIRST`\n* `\"CONNTRACK\"`: `NF_IP_PRI_CONNTRACK`\n* `\"MANGLE\"`: `NF_IP_PRI_MANGLE`\n* `\"NAT_DST\"`: `NF_IP_PRI_NAT_DST`\n* `\"FILTER\"`: `NF_IP_PRI_FILTER`\n* `\"SECURITY\"`: `NF_IP_PRI_SECURITY`\n* `\"NAT_SRC\"`: `NF_IP_PRI_NAT_SRC`\n* `\"SELINUX_LAST\"`: `NF_IP_PRI_SELINUX_LAST`\n* `\"CONNTRACK_HELPER\"`: `NF_IP_PRI_CONNTRACK_HELPER`\n* `\"LAST\"`: `NF_IP_PRI_LAST`\n\n#### `netfilter.bridge_priority`\n\n_netfilter.bridge_priority_ is a table that exports\nnetfilter bridge priority levels to Lua.\n\n* `\"FIRST\"`: `NF_BR_PRI_FIRST`\n* `\"NAT_DST_BRIDGED\"`: `NF_BR_PRI_NAT_DST_BRIDGED`\n* `\"FILTER_BRIDGED\"`: `NF_BR_PRI_FILTER_BRIDGED`\n* `\"BRNF\"`: `NF_BR_PRI_BRNF`\n* `\"NAT_DST_OTHER\"`: `NF_BR_PRI_NAT_DST_OTHER`\n* `\"FILTER_OTHER\"`: `NF_BR_PRI_FILTER_OTHER`\n* `\"NAT_SRC\"`: `NF_BR_PRI_NAT_SRC`\n* `\"LAST\"`: `NF_BR_PRI_LAST`\n\n### luaxt\n\nThe `luaxt` [userspace library](usr/lib/xtable) provides support for generating userspace code for [xtable extensions](https://inai.de/projects/xtables-addons/).\n\nTo build the library, the following steps are required:\n\n1. Go to `usr/lib/xtable` and create a `libxt_\u003cext_name\u003e.lua` file.\n2. Register your callbacks for the xtable extension by importing the library (`luaxt`) in the created file.\n3. Run `LUAXTABLE_MODULE=\u003cext_name\u003e make` to build the extension and `LUAXTABLE_MODULE=\u003cext_name\u003e make install` (as root) to install the userspace plugin to the system.\n\nNow load the extension normally using `iptables`.\n\n#### `luaxt.match(opts)`\n\n_luaxt.match()_ returns a new [luaxt](https://inai.de/projects/xtables-addons/) object for match extensions.\nThis function receives the following arguments:\n* `opts`: a table containing the following fields:\n  * `revision`: integer representing the xtable extension revision (**must** be same as used in corresponding kernel extension).\n  * `family`: address family, one of [luaxt.family](https://github.com/luainkernel/lunatik#luaxtfamily)\n  * `help`: function to be called for displaying help message for the extension.\n  * `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\"`)\n  * `print`: function to be called for printing the arguments. This function recevies `userargs` set by the `init` or `parse` function.\n  * `save`: function to be called for saving the arguments. This function recevies `userargs` set by the `init` or `parse` function.\n  * `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\"`)\n  * `final_check`: function to be called for final checking of the arguments. This function receives `flags` set by the `parse` function.\n\n#### `luaxt.target(opts)`\n\n_luaxt.target()_ returns a new [luaxt](https://inai.de/projects/xtables-addons/) object for target extensions.\nThis function receives the following arguments:\n* `opts`: a table containing the following fields:\n  * `revision`: integer representing the xtable extension revision (**must** be same as used in corresponding kernel extension).\n  * `family`: address family, one of [luaxt.family](https://github.com/luainkernel/lunatik#luaxtfamily)\n  * `help`: function to be called for displaying help message for the extension.\n  * `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\"`)\n  * `print`: function to be called for printing the arguments. This function recevies `userargs` set by the `init` or `parse` function.\n  * `save`: function to be called for saving the arguments. This function recevies `userargs` set by the `init` or `parse` function.\n  * `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\"`)\n  * `final_check`: function to be called for final checking of the arguments. This function receives `flags` set by the `parse` function.\n\n#### `luaxt.family`\n\n_luaxt.family_ is a table that exports\naddress families to Lua.\n\n* `\"UNSPEC\"`: Unspecified.\n* `\"INET\"`: Internet Protocol version 4.\n* `\"IPV4\"`: Internet Protocol version 4.\n* `\"IPV6\"`: Internet Protocol version 6.\n* `\"ARP\"`: Address Resolution Protocol.\n* `\"NETDEV\"`: Device ingress and egress path\n* `\"BRIDGE\"`: Ethernet Bridge.\n\n### `completion`\n\nThe `completion` library provides support for the [kernel completion primitives](https://docs.kernel.org/scheduler/completion.html).\n\nTask 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.\n\n#### `completion.new()`\n\n_completion.new()_ creates a new `completion` object.\n\n#### `c:complete()`\n\n_c:complete()_ signals a single thread waiting on this completion.\n\n#### `c:wait([timeout])`\n\n_c:wait()_ waits for completion of a task until the specified timeout expires.\nThe 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.\nThreads waiting for events can be interrupted by signals, for example, such as when `thread.stop` is invoked.\nTherefore, this function can return in three ways:\n* If it succeeds, it returns `true`\n* If the timeout is reached, it returns `nil, \"timeout\"`\n* If the task is interrupted, it returns `nil, \"interrupt\"`\n\n# Examples\n\n### spyglass\n\n[spyglass](examples/spyglass.lua)\nis a kernel script that implements a _keylogger_ inspired by the\n[spy](https://github.com/jarun/spy) kernel module.\nThis kernel script logs the _keysym_ of the pressed keys in a device (`/dev/spyglass`).\nIf the _keysym_ is a printable character, `spyglass` logs the _keysym_ itself;\notherwise, it logs a mnemonic of the ASCII code, (e.g., `\u003cdel\u003e` stands for `127`).\n\n#### Usage\n\n```\nsudo make examples_install          # installs examples\nsudo lunatik run examples/spyglass  # runs spyglass\nsudo tail -f /dev/spyglass          # prints the key log\nsudo sh -c \"echo 'enable=false' \u003e /dev/spyglass\"       # disable the key logging\nsudo sh -c \"echo 'enable=true' \u003e /dev/spyglass\"        # enable the key logging\nsudo sh -c \"echo 'net=127.0.0.1:1337' \u003e /dev/spyglass\" # enable network support\nnc -lu 127.0.0.1 1337 \u0026             # listen to UDP 127.0.0.1:1337\nsudo tail -f /dev/spyglass          # sends the key log through the network\n```\n\n### keylocker\n\n[keylocker](examples/keylocker.lua)\nis a kernel script that implements\n[Konami Code](https://en.wikipedia.org/wiki/Konami_Code)\nfor locking and unlocking the console keyboard.\nWhen the user types `↑ ↑ ↓ ↓ ← → ← → LCTRL LALT`,\nthe keyboard will be _locked_; that is, the system will stop processing any key pressed\nuntil the user types the same key sequence again.\n\n#### Usage\n\n```\nsudo make examples_install                     # installs examples\nsudo lunatik run examples/keylocker            # runs keylocker\n\u003c↑\u003e \u003c↑\u003e \u003c↓\u003e \u003c↓\u003e \u003c←\u003e \u003c→\u003e \u003c←\u003e \u003c→\u003e \u003cLCTRL\u003e \u003cLALT\u003e # locks keyboard\n\u003c↑\u003e \u003c↑\u003e \u003c↓\u003e \u003c↓\u003e \u003c←\u003e \u003c→\u003e \u003c←\u003e \u003c→\u003e \u003cLCTRL\u003e \u003cLALT\u003e # unlocks keyboard\n```\n\n### tap\n\n[tap](examples/tap.lua)\nis a kernel script that implements a _sniffer_ using `AF_PACKET` socket.\nIt prints destination and source MAC addresses followed by Ethernet type and the frame size.\n\n#### Usage\n\n```\nsudo make examples_install    # installs examples\nsudo lunatik run examples/tap # runs tap\ncat /dev/tap\n```\n\n### shared\n\n[shared](examples/shared.lua)\nis a kernel script that implements an in-memory key-value store using\n[rcu](https://github.com/luainkernel/lunatik#rcu),\n[data](https://github.com/luainkernel/lunatik#data),\n[socket](https://github.com/luainkernel/lunatik#socket) and\n[thread](https://github.com/luainkernel/lunatik#thread).\n\n#### Usage\n\n```\nsudo make examples_install         # installs examples\nsudo lunatik spawn examples/shared # spawns shared\nnc 127.0.0.1 90                    # connects to shared\nfoo=bar                            # assigns \"bar\" to foo\nfoo                                # retrieves foo\nbar\n^C                                 # finishes the connection\n```\n\n### echod\n\n[echod](examples/echod)\nis an echo server implemented as kernel scripts.\n\n#### Usage\n\n```\nsudo make examples_install               # installs examples\nsudo lunatik spawn examples/echod/daemon # runs echod\nnc 127.0.0.1 1337\nhello kernel!\nhello kernel!\n```\n\n### systrack\n\n[systrack](examples/systrack.lua)\nis a kernel script that implements a device driver to monitor system calls.\nIt prints the amount of times each [system call](examples/systrack.lua#L29)\nwas called since the driver has been installed.\n\n#### Usage\n\n```\nsudo make examples_install         # installs examples\nsudo lunatik run examples/systrack # runs systracker\ncat /dev/systrack\nwritev: 0\nclose: 1927\nwrite: 1085\nopenat: 2036\nread: 4131\nreadv: 0\n```\n\n### filter\n\n[filter](examples/filter) is a kernel extension composed by\na XDP/eBPF program to filter HTTPS sessions and\na Lua kernel script to filter [SNI](https://datatracker.ietf.org/doc/html/rfc3546#section-3.1) TLS extension.\nThis kernel extension drops any HTTPS request destinated to a\n[blacklisted](examples/filter/sni.lua#L35) server.\n\n#### Usage\n\nCompile and install `libbpf`, `libxdp` and `xdp-loader`:\n\n```sh\nmkdir -p \"${LUNATIK_DIR}\" ; cd \"${LUNATIK_DIR}\"  # LUNATIK_DIR must be set to the same value as above (Setup section)\ngit clone --depth 1 --recurse-submodules https://github.com/xdp-project/xdp-tools.git\ncd xdp-tools/lib/libbpf/src\nmake\nsudo DESTDIR=/ make install\ncd ../../../\nmake libxdp\ncd xdp-loader\nmake\nsudo make install\n```\n\nCome back to this repository, install and load the filter:\n\n```sh\ncd ${LUNATIK_DIR}/lunatik                    # cf. above\nsudo make btf_install                        # needed to export the 'bpf_luaxdp_run' kfunc\nsudo make examples_install                   # installs examples\nmake ebpf                                    # builds the XDP/eBPF program\nsudo make ebpf_install                       # installs the XDP/eBPF program\nsudo lunatik run examples/filter/sni false   # runs the Lua kernel script\nsudo xdp-loader load -m skb \u003cifname\u003e https.o # loads the XDP/eBPF program\n```\n\nFor example, testing is easy thanks to [docker](https://www.docker.com).\nAssuming docker is installed and running:\n\n- in a terminal:\n```sh\nsudo xdp-loader load -m skb docker0 https.o\nsudo journalctl -ft kernel\n```\n- in another one:\n```sh\ndocker run --rm -it alpine/curl https://ebpf.io\n```\n\nThe system logs (in the first terminal) should display `filter_sni: ebpf.io DROP`, and the\n`docker run…` should return `curl: (35) OpenSSL SSL_connect: SSL_ERROR_SYSCALL in connection to ebpf.io:443`.\n\n### filter in MoonScript\n\n[This other sni filter](https://github.com/luainkernel/snihook) uses netfilter api.\n\n### dnsblock\n\n[dnsblock](examples/dnsblock) is a kernel script that uses the lunatik xtable library to filter DNS packets.\nThis script drops any outbound DNS packet with question matching the blacklist provided by the user. By default, it will block DNS resolutions for the domains `github.com` and `gitlab.com`.\n\n#### Usage\n\n1. Using legacy iptables\n```\nsudo make examples_install              # installs examples\ncd examples/dnsblock\nmake                                    # builds the userspace extension for netfilter\nsudo make install   \t\t\t\t\t# installs the extension to Xtables directory\nsudo lunatik run examples/dnsblock/dnsblock false\t# runs the Lua kernel script\nsudo iptables -A OUTPUT -m dnsblock -j DROP     \t# this initiates the netfilter framework to load our extension\n```\n\n2. Using new netfilter framework ([luanetfilter](https://github.com/luainkernel/lunatik#netfilter))\n\n```\nsudo make examples_install              # installs examples\nsudo lunatik run examples/dnsblock/nf_dnsblock false\t# runs the Lua kernel script\n```\n\n### dnsdoctor\n\n[dnsdoctor](examples/dnsdoctor) is a kernel script that uses the lunatik xtable library to change the DNS response\nfrom Public IP to a Private IP if the destination IP matches the one provided by the user. For example, if the user\nwants 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.\n\n#### Usage\n\n1. Using legacy iptables\n```\nsudo make examples_install              # installs examples\ncd examples/dnsdoctor\nsetup.sh                                # sets up the environment\n\n# test the setup, a response with IP 192.168.10.1 should be returned\ndig lunatik.com\n\n# run the Lua kernel script\nsudo lunatik run examples/dnsdoctor/dnsdoctor false\n\n# build and install the userspace extension for netfilter\nmake\nsudo make install\n\n# add rule to the mangle table\nsudo iptables -t mangle -A PREROUTING -p udp --sport 53 -j dnsdoctor\n\n# test the setup, a response with IP 10.1.2.3 should be returned\ndig lunatik.com\n\n# cleanup\nsudo iptables -t mangle -D PREROUTING -p udp --sport 53 -j dnsdoctor # remove the rule\nsudo lunatik unload\ncleanup.sh\n```\n\n2. Using new netfilter framework ([luanetfilter](https://github.com/luainkernel/lunatik#netfilter))\n```\nsudo make examples_install              # installs examples\nexamples/dnsdoctor/setup.sh             # sets up the environment\n\n# test the setup, a response with IP 192.168.10.1 should be returned\ndig lunatik.com\n\n# run the Lua kernel script\nsudo lunatik run examples/dnsdoctor/nf_dnsdoctor false\n\n# test the setup, a response with IP 10.1.2.3 should be returned\ndig lunatik.com\n\n# cleanup\nsudo lunatik unload\nexamples/dnsdoctor/cleanup.sh\n```\n\n## References\n\n* [Scripting the Linux Routing Table with Lua](https://netdevconf.info/0x17/sessions/talk/scripting-the-linux-routing-table-with-lua.html)\n* [Lua no Núcleo](https://www.youtube.com/watch?v=-ufBgy044HI) (Portuguese)\n* [Linux Network Scripting with Lua](https://legacy.netdevconf.info/0x14/session.html?talk-linux-network-scripting-with-lua)\n* [Scriptables Operating Systems with Lua](https://www.netbsd.org/~lneto/dls14.pdf)\n\n## License\n\nLunatik is dual-licensed under [MIT](LICENSE-MIT) or [GPL-2.0-only](LICENSE-GPL).\n\n[Lua](https://github.com/luainkernel/lua) submodule is licensed under MIT.\nFor more details, see its [Copyright Notice](https://github.com/luainkernel/lua/blob/lunatik/lua.h#L530-L556).\n\n[Klibc](https://github.com/luainkernel/klibc) submodule is dual-licensed under BSD 3-Clause or GPL-2.0-only.\nFor more details, see its [LICENCE](https://github.com/luainkernel/klibc/blob/lunatik/usr/klibc/LICENSE) file.\n\n","funding_links":[],"categories":["C"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fluainkernel%2Flunatik","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fluainkernel%2Flunatik","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fluainkernel%2Flunatik/lists"}