Lunatik es un marco para escribir el núcleo de Linux con Lua. Está compuesto por el intérprete Lua modificado para ejecutar en el núcleo; un controlador de dispositivo (escrito en lua =)) y una herramienta de línea de comando para cargar y ejecutar scripts y administrar entornos de tiempo de ejecución desde el espacio del usuario; una API C para cargar y ejecutar scripts y administrar entornos de tiempo de ejecución desde el núcleo; y API de Lua para las instalaciones vinculantes del núcleo a los scripts de Lua.
Aquí hay un ejemplo de un controlador de dispositivo de caracteres escrito en Lua usando Lunatik para generar caracteres imprimibles ASCII aleatorios:
-- /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 )Instalar dependencias (aquí para Debian/Ubuntu, para adaptarse a la distribución de uno):
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 Compilar e instalar lunatik :
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 Una vez hecho hecho, el script debian_kernel_postinst_lunatik.sh de herramientas/puede copiarse en /etc/kernel/postinst.d/ : Esto garantiza que lunatik (y también los Libs necesarios xdp ) se compilará en la actualización del kernel.
sudo lunatik # execute Lunatik REPL
Lunatik 3.5 Copyright (C) 2023-2024 ring-0 Ltda.
> return 42 -- execute this line in the kernel
42
usage: lunatik [load | unload | reload | status | list] [run | spawn | stop < script > ]load : Módulos de núcleo Lunatik de cargaunload : descargar módulos de núcleo lunatikreload : Recargar módulos de núcleo Lunatikstatus : Muestre qué módulos de kernel lunatik están cargados actualmentelist : Muestre qué entornos de tiempo de ejecución se están ejecutando actualmenterun : cree un nuevo entorno de tiempo de ejecución para ejecutar script /lib/modules/lua/<script>.luaspawn : cree un nuevo entorno de tiempo de ejecución y genere un hilo para ejecutar el script /lib/modules/lua/<script>.luastop : detener el entorno de tiempo de ejecución creado para ejecutar el script <script>default : iniciar un reple (leer - Eval - bucle de impresión) Lunatik 3.4 se basa en Lua 5.4 adaptado para funcionar en el núcleo.
Lunatik no es compatible con la aritmética de punto flotante, por lo tanto, no admite __div ni __pow metametods y el número de tipo solo tiene el entero de subtipo.
Lunatik no admite las bibliotecas IO y OS, y los identificadores dados de las siguientes bibliotecas:
Lunatik modifica los siguientes identificadores:
"Lua 5.4-kernel" ."/lib/modules/lua/?.lua;/lib/modules/lua/?/init.lua" .Lunatik no es compatible con Lual_Stream, Lual_Execresult, Lual_Fileresult, Luaopen_io y Luaopen_os.
Lunatik modifica lual_openlibs para eliminar Luaopen_io y Luaopen_os.
#include <lunatik.h> int lunatik_runtime ( lunatik_object_t * * pruntime , const char * script , bool sleep ); lunatik_runtime () crea un nuevo entorno runtime , luego carga y ejecuta el script /lib/modules/lua/<script>.lua como el punto de entrada para este entorno. Solo debe llamarse desde el contexto del proceso . El entorno runtime es un objeto Lunatik que posee un estado de lua. Los objetos Lunatik son datos de usuario LUA especiales que también contienen un tipo de bloqueo y un contador de referencia. Si sleep es verdadero , lunatik_runtime () usará un mutex para bloquear el entorno runtime y el indicador GFP_Kernel para asignar una nueva memoria más adelante en las llamadas lunatik_run (). De lo contrario, usará un spinlock y gfp_atomic. lunatik_runtime () abre las bibliotecas estándar de Lua presentes en Lunatik. Si tiene éxito, lunatik_runtime () establece la dirección apuntada por pruntime y el espacio adicional de Lua con un puntero para el nuevo entorno de runtime creado, establece el contador de referencia a 1 y luego devuelve 0 . De lo contrario, devuelve -ENOMEM , si no hay memoria insuficiente disponible; o -EINVAL , si no se carga o ejecuta el script .
-- /lib/modules/lua/mydevice.lua
function myread ( len , off )
return " 42 "
end static lunatik_object_t * runtime ;
static int __init mydevice_init ( void )
{
return lunatik_runtime ( & runtime , "mydevice" , true);
} int lunatik_stop ( lunatik_object_t * runtime ); lunatik_stop () cierra el estado LUA creado para este entorno runtime y disminuye el contador de referencia. Una vez que el contador de referencia se disminuye a cero, se libera el tipo de bloqueo y la memoria asignada para el entorno runtime . Si se ha lanzado el entorno runtime , devuelve 1 ; De lo contrario, devuelve 0 .
void lunatik_run ( lunatik_object_t * runtime , < inttype > ( * handler )(...), < inttype > & ret , ...); lunatik_run () bloquea el entorno runtime y llama al handler que pasa el estado de Lua asociado como el primer argumento seguido de los argumentos variádicos. Si el estado de Lua se ha cerrado, ret está configurado con -ENXIO ; De lo contrario, ret se establece con el resultado de la llamada handler(L, ...) . Luego, restaura la pila Lua y desbloquea el entorno runtime . Se define como una macro.
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 ( "%sn" , 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 ;
} void lunatik_getobject ( lunatik_object_t * object ); lunatik_getObject () incrementa el contador de referencia de este object (por ejemplo, entorno runtime ).
int lunatik_putobject ( lunatik_object_t * object ); lunatik_putObject () disminuye el contador de referencia de este object (por ejemplo, entorno runtime ). Si el object ha sido lanzado, devuelve 1 ; De lo contrario, devuelve 0 .
lunatik_object_t * lunatik_toruntime ( lua_State * L ); lunatik_toruntime () Devuelve el entorno runtime referenciado por el espacio adicional de L '.
La Biblioteca lunatik proporciona soporte para cargar y ejecutar scripts y administrar entornos de tiempo de ejecución desde LUA.
lunatik.runtime(script [, sleep]) lunatik.runtime () crea un nuevo entorno de tiempo de ejecución, luego carga y ejecuta el script /lib/modules/lua/<script>.lua como el punto de entrada para este entorno. Devuelve un objeto Lunatik que representa el entorno runtime . Si sleep es verdadero u omitido, usará un mutex y gfp_kernel; De lo contrario, usará un spinlock y gfp_atomic. lunatik.runtime () abre las bibliotecas estándar de Lua presentes en Lunatik.
runtime:stop() tiempo de ejecución: stop () Detener el entorno runtime y borrar su referencia del objeto de tiempo de ejecución.
runtime:resume([obj1, ...]) tiempo de ejecución: resume () reanuda la ejecución de un runtime . Los valores obj1, ... se pasan como los argumentos a la función devueltos en la creación runtime . Si el runtime ha cedido, resume() lo reinicia; Los valores obj1, ... se pasan como los resultados del rendimiento.
La biblioteca device proporciona soporte para escribir controladores de dispositivos de caracteres en LUA.
device.new(driver) Device.new () devuelve un nuevo objeto device e instala su driver en el sistema. El driver debe definirse como una tabla que contiene el siguiente campo:
name : cadena Definición del nombre del dispositivo; Se utiliza para crear el archivo del dispositivo (por ejemplo, /dev/<name> ). La tabla driver puede contener opcionalmente los siguientes campos:
read : Función de devolución de llamada para manejar la operación de lectura en el archivo del dispositivo. Recibe la tabla driver como el primer argumento seguido de dos enteros, la length a leer y la offset del archivo. Debe devolver una cadena y, opcionalmente, el updated offset . Si la longitud de la cadena devuelta es mayor que la length solicitada, la cadena se corregirá a esa length . Si no se devuelve el updated offset , el offset se actualizará con offset + length .write : función de devolución de llamada para manejar la operación de escritura en el archivo del dispositivo. Recibe la tabla driver como el primer argumento seguido de la cadena que se escribirá y un entero como offset del archivo. Puede devolver opcionalmente la length escrita seguida del updated offset . Si la longitud devuelta es mayor que la length solicitada, la longitud devuelta se corregirá. Si no se devuelve el updated offset , el offset se actualizará con offset + length .open : función de devolución de llamada para manejar la operación abierta en el archivo del dispositivo. Recibe la tabla driver y se espera que no devuelva nada.release : función de devolución de llamada para manejar la operación de lanzamiento en el archivo del dispositivo. Recibe la tabla driver y se espera que no devuelva nada.mode : un entero que especifica el modo de archivo del dispositivo. Si no se define una devolución de llamada de operación, el device devuelve -ENXIO a VFS en su acceso.
device.stop(dev) , dev:stop() Device.stop () Elimina un driver de dispositivo especificado por el objeto dev del sistema.
La biblioteca linux proporciona soporte para algunas instalaciones de kernel de Linux.
linux.random([m [, n]])Linux.random () imita el comportamiento de Math.Random, pero vinculante <linux/random.h> s get_random_u32 () y get_random_u64 () apis.
Cuando se llama sin argumentos, produce un entero con todos los bits (pseudo) aleatorios. Cuando se les llama con dos enteros m y n , Linux.Random () devuelve un entero pseudo-random con distribución uniforme en el rango [m, n] . La llamada math.random(n) , para una n positiva, es equivalente a math.random(1, n) .
linux.statLinux.stat es una tabla que exporta los indicadores enteros <linux/stat.h> enteros a Lua.
"IRWXUGO" : permiso para leer , escribir y ejecutar para el usuario , el grupo y otros ."IRUGO" : permiso solo para leer para el usuario , grupo y otro ."IWUGO" : permiso solo para escribir para el usuario , grupo y otro ."IXUGO" : permiso solo para ejecutar para el usuario , grupo y otros . linux.schedule([timeout [, state]]) Linux.schedule () establece el state de tarea actual y hace que duerma hasta que hayan transcurrido los milisegundos timeout . Si se omite timeout , usa MAX_SCHEDULE_TIMEOUT . Si se omite state , usa task.INTERRUPTIBLE .
linux.taskLinux.Task es una tabla que exporta las banderas de estado de la tarea a Lua.
"RUNNING" : la tarea se está ejecutando en una CPU o esperando ser ejecutada."INTERRUPTIBLE" : la tarea está esperando una señal o un recurso (dormir)."UNINTERRUPTIBLE" : se comporta como "interrupción" con la excepción de que la señal no despertará la tarea."KILLABLE" : se comporta como "ininterrumpido" con la excepción de que las señales fatales despertarán la tarea."IDLE" : se comporta como "ininterrumpido" con la excepción de que evita la contabilidad de loadavg. linux.time()linux.time () Devuelve la hora actual en nanosegundos desde la época.
linux.errnoLinux.errno es una tabla que exporta <UAPI/ASM-Generic/Errno-Base.h> Flags a Lua.
"PERM" : operación no permitida."NOENT" : no hay dicho archivo o directorio."SRCH" : no hay tal proceso."INTR" : llamada de sistema interrumpido."IO" : Error de E/S."NXIO" : no hay tal dispositivo o dirección."2BIG" : lista de argumentos demasiado larga."NOEXEC" : error de formato ejecutivo."BADF" : número de archivo malo."CHILD" : no hay procesos para niños."AGAIN" : intente de nuevo."NOMEM" : fuera de memoria."ACCES" : permiso denegado."FAULT" : mala dirección."NOTBLK" : dispositivo de bloque requerido."BUSY" : dispositivo o recurso ocupado."EXIST" : Existe el archivo."XDEV" : enlace de dispositivo cruzado."NODEV" : no hay tal dispositivo."NOTDIR" : No es un directorio."ISDIR" : es un directorio."INVAL" : argumento inválido."NFILE" : desbordamiento de la tabla de archivos."MFILE" : demasiados archivos abiertos."NOTTY" : no una máquina de escribir."TXTBSY" : archivo de texto ocupado."FBIG" : archivo demasiado grande."NOSPC" : No queda espacio en el dispositivo."SPIPE" : búsqueda ilegal."ROFS" : sistema de archivos de solo lectura."MLINK" : demasiados enlaces."PIPE" : tubería rota."DOM" : Matemádico Argumento fuera del dominio de func."RANGE" : resultado matemático no representable. linux.hton16(num)Linux.hton16 () convierte el pedido de bytes de host para el pedido de bytes de red para un entero de 16 bits.
linux.hton32(num)Linux.hton32 () convierte el pedido de bytes de host para el pedido de bytes de red para un entero de 32 bits.
linux.hton64(num)Linux.hton64 () convierte el pedido de bytes de host para el pedido de bytes de red para un entero de 64 bits.
linux.ntoh16(num)Linux.ntoh16 () convierte el orden de bytes de red para alojar el pedido de bytes para un entero de 16 bits.
linux.ntoh32(num)Linux.ntoh32 () convierte el orden de bytes de red para alojar el pedido de bytes para un entero de 32 bits.
linux.ntoh64(num)Linux.ntoh64 () convierte el pedido de bytes de red para alojar el pedido de bytes para un entero de 64 bits.
linux.htobe16(num)Linux.htobe16 () convierte el orden de bytes host en orden de byte grande endian para un entero de 16 bits.
linux.htobe32(num)Linux.htobe32 () convierte el orden de bytes host en orden de byte grande endian para un entero de 32 bits.
linux.htobe64(num)Linux.htobe64 () convierte el orden de bytes host en orden de bytes de grandes endian para un entero de 64 bits.
linux.be16toh(num)Linux.Be16ToH () convierte el orden de bytes de grandes endian para alojar el orden de bytes para un entero de 16 bits.
linux.be32toh(num)Linux.Be32ToH () convierte el orden de bytes de Big-Endian para alojar el orden de bytes para un entero de 32 bits.
linux.be64toh(num)Linux.Be64ToH () convierte el orden de bytes de Big-Endian para alojar el pedido de bytes para un entero de 64 bits.
linux.htole16(num)Linux.htole16 () convierte el orden de bytes host en orden de byte pequeño endian para un entero de 16 bits.
linux.htole32(num)Linux.htole32 () convierte el orden de bytes host en orden de byte pequeño endian para un entero de 32 bits.
linux.htole64(num)Linux.htole64 () convierte el orden de bytes host en orden de byte pequeño endian para un entero de 64 bits.
linux.le16toh(num)Linux.le16toh () convierte el pedido de bytes de bytes pequeños para alojar el orden de bytes para un entero de 16 bits.
linux.le32toh(num)Linux.LE32TOH () convierte el orden de bytes de Little-Endian para alojar el orden de bytes para un entero de 32 bits.
linux.le64toh(num)Linux.le64toh () convierte el pedido de bytes de byte pequeño para alojar el orden de bytes para un entero de 64 bits.
La biblioteca notifier proporciona soporte para las cadenas de notificadores del núcleo.
notifier.keyboard(callback) Notifier.Keyboard () Devuelve un nuevo objeto notifier de teclado y lo instala en el sistema. La función callback se llama cada vez que ocurre un evento de teclado de consola (por ejemplo, se ha presionado o lanzado una tecla). Esta callback recibe los siguientes argumentos:
event : los eventos disponibles están definidos por la tabla Notificador.kbd.down : true , si se presiona la tecla; false , si se lanza.shift : true , si se mantiene la clave de cambio; false , de lo contrario.key : KeyCode o KeySym según event . La función callback podría devolver los valores definidos por la tabla Notificador.notify.
notifier.kbdNotifier.kbd es una tabla que exporta las banderas de KBD a Lua.
"KEYCODE" : Código de teclado del teclado, llamado antes de cualquier otro."UNBOUND_KEYCODE" : Código de teclado del teclado que no está vinculado a ningún otro."UNICODE" : teclado Unicode."KEYSYM" : teclado Keysym ."POST_KEYSYM" : Llamado después de la interpretación de teclado del teclado. notifier.netdevice(callback) Notifier.netDevice () Devuelve un nuevo objeto NetDevice notifier y lo instala en el sistema. La función callback se llama cada vez que ocurre un evento de Evice de NetDevice de consola (por ejemplo, se ha conectado o desconectado una interfaz de red). Esta callback recibe los siguientes argumentos:
event : Los eventos disponibles están definidos por la tabla Notifier.netdev.name : el nombre del dispositivo. La función callback podría devolver los valores definidos por la tabla Notificador.notify.
notifier.netdevNotifier.netdev es una tabla que exporta las banderas NetDev a LUA.
notifier.notifyNotifier.notify es una tabla que exporta notificar a los indicadores a Lua.
"DONE" : No me importa."OK" : me queda bien."BAD" : Acción Bad/Veto."STOP" : forma limpia de regresar del notificador y detener más llamadas. notfr:delete() nofr: delete () elimina un notifier especificado por el objeto notfr del sistema.
La biblioteca socket proporciona soporte para el manejo de redes de kernel. Esta biblioteca se inspiró en el proyecto GSOC de Chengzhi Tan.
socket.new(family, type, protocol) Socket.new () crea un nuevo objeto socket . Esta función recibe los siguientes argumentos:
family : las familias de dirección disponibles están definidas por la tabla de enchufes.sock : Los tipos disponibles están presentes en la tabla de buceo.protocol : los protocolos disponibles están definidos por la tabla de enchufes. socket.afSocket.af es una tabla que exporta a las familias (AF) a Lua.
"UNSPEC" : no especificado."UNIX" : enchufes de dominio Unix."LOCAL" : Nombre Posix para AF_UNIX."INET" : Protocolo de IP de Internet."AX25" : Axateur Radio Ax.25."IPX" : Novell IPX."APPLETALK" : Appletletk DDP."NETROM" : red de radio amateur/ROM."BRIDGE" : puente multiprotocol."ATMPVC" : ATM PVCS."X25" : Reservado para el proyecto X.25."INET6" : IP versión 6."ROSE" : Amateur Radio X.25 PLP."DEC" : Reservado para el proyecto Decnet."NETBEUI" : reservado para el proyecto 802.2LLC."SECURITY" : devolución de llamada de seguridad Pseudo AF."KEY" : API de administración de teclas PF_KEY."NETLINK" : NetLink."ROUTE" : alias para emular 4.4BSD."PACKET" : Familia de paquetes."ASH" : Ash."ECONET" : Acorn Econet."ATMSVC" : ATM SVCS."RDS" : HDS Sockets."SNA" : Linux SNA Project (¡nueces!)."IRDA" : Sockets Irda."PPPOX" : enchufes PPPOX."WANPIPE" : enchufes de la API Wanpipe."LLC" : Linux LLC."IB" : dirección nativa de infiniband."MPLS" : MPLS."CAN" : red de área del controlador."TIPC" : Sockets Tipc."BLUETOOTH" : enchufes Bluetooth."IUCV" : Sockets de IUCV."RXRPC" : enchufes RXRPC."ISDN" : Sockets Misdn."PHONET" : Sockets Phonet."IEEE802154" : Sockets IEEE802154."CAIF" : Sockets Caif."ALG" : enchufes de algoritmo."NFC" : Sockets NFC."VSOCK" : Vsockets."KCM" : multiplexor de conexión de kernel."QIPCRTR" : Router Qualcomm IPC."SMC" : número de reserva para la familia de protocolo PF_SMC que reutiliza la familia de direcciones AF_INET."XDP" : enchufes XDP."MCTP" : Protocolo de transporte de componentes de gestión."MAX" : máximo. socket.sockSocket.sock es una tabla que exporta los tipos de socket (SOCK):
"STREAM" : flujo (conexión) socket."DGRAM" : socket datagram (Conn.less)."RAW" : enchufe crudo."RDM" : Mensaje de conflicto confiable."SEQPACKET" : enchufe de paquetes secuencial."DCCP" : enchufe del protocolo de control de congestión de datagrama."PACKET" : forma específica de Linux de obtener paquetes en el nivel de desarrollo.y banderas (calcetín):
"CLOEXEC" : N/A."NONBLOCK" : N/A. socket.ipprotoSocket.ipproto es una tabla que exporta los protocolos IP (ipproto) a Lua.
"IP" : protocolo ficticio para TCP."ICMP" : Protocolo de mensajes de control de Internet."IGMP" : Protocolo de gestión de grupos de Internet."IPIP" : Túneles IPIP (túneles KA9Q más antiguos usan 94)."TCP" : Protocolo de control de transmisión."EGP" : protocolo de puerta de enlace exterior."PUP" : Protocolo de cachorros."UDP" : Protocolo de datagrama de usuario."IDP" : Protocolo XNS IDP."TP" : So Transport Protocol Clase 4."DCCP" : Protocolo de control de congestión de datagrama."IPV6" : túnel IPv6-in-IPv4."RSVP" : protocolo RSVP."GRE" : Cisco GRE Tunnels (RFC 1701,1702)."ESP" : Protocolo de carga útil de seguridad de encapsulación."AH" : Protocolo de encabezado de autenticación."MTP" : Protocolo de transporte de multidifusión."BEETPH" : encabezado de pseudo de opción IP para remolacha."ENCAP" : encabezado de encapsulación."PIM" : multidifusión independiente del protocolo."COMP" : protocolo de encabezado de compresión."SCTP" : Protocolo de transporte de control de corriente."UDPLITE" : UDP-Lite (RFC 3828)."MPLS" : MPLS en IP (RFC 4023)."ETHERNET" : Ethernet-Within-IPV6 Encapsulación."RAW" : paquetes IP crudos."MPTCP" : conexión TCP múltiple. sock:close() SOCK: Close () Elimina el objeto sock del sistema.
sock:send(message, [addr [, port]]) Sock: Send () envía un message de cadena a través del sock de socket. Si la familia de direcciones sock es af.INET , entonces espera los siguientes argumentos:
addr : integer que describe la dirección IPv4 de destino.port : integer que describe el puerto IPv4 de destino.De lo contrario:
addr : cadena empaquetada que describe la dirección de destino. sock:receive(length, [flags [, from]]) SOCK: Recibe () recibe una cadena con bytes hasta length a través del sock del socket. Los indicadores de mensajes disponibles están definidos por la tabla Socket.msg. Si from es true , devuelve el mensaje recibido seguido de la dirección del par. De lo contrario, solo devuelve el mensaje recibido.
socket.msgSocket.msg es una tabla que exporta los indicadores de mensajes a LUA.
"OOB" : N/A."PEEK" : N/A."DONTROUTE" : N/A."TRYHARD" : sinónimo de "DONTROUTE" para Decnet."CTRUNC" : N/A."PROBE" : No envíe. Solo sondea la ruta FE para MTU."TRUNC" : N/A."DONTWAIT" : IO sin bloqueo."EOR" : Fin del registro."WAITALL" : espere una solicitud completa."FIN" : N/A."SYN" : N/A."CONFIRM" : Confirmar la validez de la ruta."RST" : N/A."ERRQUEUE" : obtenga un mensaje de la cola de error."NOSIGNAL" : no genere siglo."MORE" : El remitente enviará más."WAITFORONE" : Recvmmsg (): bloquee hasta que se dispongan de más de 1+ paquetes."SENDPAGE_NOPOLICY" : sendPage () interno: no aplique la política."SENDPAGE_NOTLAST" : sendPage () interno: no la última página."BATCH" : sendmmsg (): vendrán más mensajes."EOF" : N/A."NO_SHARED_FRAGS" : sendPage () interno: los frags de página no se comparten."SENDPAGE_DECRYPTED" : SendPage () interno: la página puede llevar texto plano y requerir cifrado."ZEROCOPY" : use datos de usuario en la ruta del núcleo."FASTOPEN" : envíe datos en TCP SYN."CMSG_CLOEXEC" : Establecer Close_on_Exec para el descriptor de archivo recibido a través de SCM_Rights. sock:bind(addr [, port]) Sock: Bind () une el sock de enchufe a una dirección dada. Si la familia de direcciones sock es af.INET , entonces espera los siguientes argumentos:
addr : integer que describe la dirección IPv4 del host.port : integer que describe el puerto IPv4 del host.De lo contrario:
addr : cadena empaquetada que describe la dirección del host. sock:listen([backlog]) SOCK: Listen () mueve el sock al estado de escucha.
backlog : tamaño de cola de conexiones pendientes. Si se omite, usa SomaxConn como predeterminado. sock:accept([flags]) SOCK: Aceptar () acepta una conexión en Socket sock . Devuelve un nuevo objeto socket . Las banderas disponibles están presentes en la tabla Socket.
sock:connect(addr [, port] [, flags]) SOCK: Connect () Conecte el sock del socket al addr de la dirección. Si la familia de direcciones sock es af.INET , entonces espera los siguientes argumentos:
addr : integer que describe la dirección IPv4 de destino.port : integer que describe el puerto IPv4 de destino.De lo contrario:
addr : cadena empaquetada que describe la dirección de destino.Las banderas disponibles están presentes en la tabla Socket.
Para los sockets de datagrama, addr es la dirección a la que se envían los datagramas de forma predeterminada y la única dirección desde la que se reciben los datagramas. Para los enchufes de transmisión, intenta conectarse a addr .
sock:getsockname() Calcetín: GetSockName () Obtenga la dirección en la que está vinculado el sock . Si la familia de direcciones sock es af.INET , entonces devuelve lo siguiente:
addr : integer que describe la dirección IPv4 limitada.port : integer que describe el puerto IPv4 limitado.De lo contrario:
addr : cadena empaquetada que describe la dirección limitada. sock:getpeername() Calcetín: GetPeername () Obtenga la dirección en la que está conectado el sock del socket. Si la familia de direcciones sock es af.INET , entonces devuelve lo siguiente:
addr : integer que describe la dirección IPv4 de la par.port : integer que describe el puerto IPv4 de la par.De lo contrario:
addr : cadena empaquetada que describe la dirección de la par. La biblioteca socket.inet proporciona soporte para enchufes IPv4 de alto nivel.
inet.tcp() inet.tcp () crea un nuevo socket usando la familia de direcciones AF.inet, el tipo de sock.stream y el protocolo ipproto.tcp. Anula los métodos socket para usar direcciones como notación de números y puntos (por ejemplo, "127.0.0.1" ), en lugar de enteros.
inet.udp() inet.udp () crea un nuevo socket usando la familia de direcciones AF.inet, el tipo de sock.dgram y el protocolo ipproto.udp. Anula los métodos socket para usar direcciones como notación de números y puntos (por ejemplo, "127.0.0.1" ), en lugar de enteros.
udp:receivefrom(length [, flags]) UDP: RecibeFrom () es solo un alias para sock:receive(length, flags, true) .
La biblioteca rcu proporciona soporte para el mecanismo de sincronización de la actualización de copia de lectura del kernel (RCU). Esta biblioteca se inspiró en el proyecto GSOC de Caio Messias.
rcu.table([size]) rcu.table () crea un nuevo objeto rcu.table que une la tabla hash genérica del núcleo. Esta función recibe como argumento el número de cubos redondeados a la siguiente potencia de 2. El tamaño predeterminado es 1024 . La clave debe ser una cadena y el valor debe ser un objeto lunatik o nulo.
La biblioteca thread proporciona soporte para las primitivas de hilo del kernel.
thread.run(runtime, name) Thread.run () crea un nuevo objeto thread y lo despierta. Esta función recibe los siguientes argumentos:
runtime : el entorno de tiempo de ejecución para ejecutar una tarea en el hilo del kernel creado. La tarea debe especificarse devolviendo una función en el script cargado en el entorno runtime .name : cadena que representa el nombre del hilo (por ejemplo, como se muestra en ps ). thread.shouldstop() thread.shouldstop () Devuelve true si se llamó a thread.stop (); De lo contrario, devuelve false .
thread.current() Thread.Current () Devuelve un objeto thread que representa la tarea actual.
thrd:stop() thrd: stop () establece hilo.shouldstop () en el hilo thrd para devolver verdadero, despierta thrd y espera a que salga.
thrd:task() thrd: task () Devuelve una tabla que contiene la información de la tarea de este thread (por ejemplo, "CPU", "Comando", "PID" y "TGID").
La biblioteca fib proporciona soporte para la base de información de reenvío del núcleo.
fib.newrule(table, priority)fib.newrule () une el kernel fib_nl_newrule API; Crea una nueva regla FIB que coincide con la tabla de enrutamiento especificada con la priorioty especificada. Esta función es similar a la regla IP del comando del espacio de usuario AGREGADO proporcionado por iProute2.
fib.delrule(table, priority)fib.delrule () une el kernel fib_nl_delrule API; Elimina una regla FIB que coincida con la tabla de enrutamiento especificada con la priorioty especificada. Esta función es similar a la regla IP del comando del espacio de usuarios Del proporcionado por iProute2.
La biblioteca data proporciona soporte para vincular la memoria del sistema a LUA.
data.new(size) data.new () crea un nuevo objeto data que asigna bytes size .
d:getnumber(offset) D: getNumber () extrae un lua_integer de la memoria referenciada por un objeto data y un offset de bytes, comenzando desde cero.
d:setnumber(offset, number) D: setNumber () Inserte un number LUA_INTEGER en la memoria a la que se hace referencia un objeto data y un offset de bytes, comenzando desde cero.
d:getbyte(offset) D: getByte () extrae un byte de la memoria a la que se hace referencia un objeto data y un offset de bytes, comenzando desde cero.
d:setbyte(offset, byte) D: setByte () Inserte un byte en la memoria referenciada por un objeto data y un offset de bytes, comenzando desde cero.
d:getstring(offset[, length]) D: GetString () extrae una cadena con bytes length de la memoria a la que se hace referencia un objeto data y un offset de bytes, comenzando desde cero. Si se omite length , extrae todos los bytes desde offset hasta el final de los data .
d:setstring(offset, s) D: setString () Inserte la cadena s en la memoria referenciada por un objeto data y un offset de bytes, comenzando desde cero.
d:getint8(offset) D: GetInt8 (D, Offset) extrae un entero de 8 bits firmado de la memoria a la que se hace referencia un objeto data y un offset de bytes, comenzando desde cero.
d:setint8(offset, number) D: SetInt8 () inserta un número de 8 bits firmado en la memoria a la que se hace referencia un objeto data y un offset de bytes, comenzando desde cero.
d:getuint8(offset) D: GetuInt8 () extrae un entero de 8 bits sin firmar de la memoria a la que se hace referencia un objeto data y un offset de bytes, comenzando desde cero.
d:setuint8(offset, number) D: setuInt8 () inserta un número de 8 bits sin firmar en la memoria a la que se hace referencia un objeto data y un offset de bytes, comenzando desde cero.
d:getint16(offset) D: GetInt16 () extrae un entero de 16 bits firmado de la memoria a la que se hace referencia un objeto data y un offset de bytes, comenzando desde cero.
d:setint16(offset, number) D: SetInt16 () inserta un número firmado de 16 bits en la memoria a la que se hace referencia un objeto data y un offset de bytes, comenzando desde cero.
d:getuint16(offset) D: getuint16 () extrae un entero de 16 bits sin firmar de la memoria a la que se hace referencia un objeto data y un offset de bytes, comenzando desde cero.
d:setuint16(offset, number) D: SetuInt16 () inserta un número de 16 bits sin firmar en la memoria a la que se hace referencia un objeto data y un offset de bytes, comenzando desde cero.
d:getint32(offset) D: GetInt32 () extrae un entero de 32 bits firmado de la memoria a la que se hace referencia un objeto data y un offset de bytes, comenzando desde cero.
d:setint32(offset, number) D: SetInt32 () inserta un número firmado de 32 bits en la memoria a la que se hace referencia un objeto data y un offset de bytes, comenzando desde cero.
d:getuint32(offset) D: getuint32 () extrae un entero de 32 bits sin firmar de la memoria a la que se hace referencia un objeto data y un offset de bytes, comenzando desde cero.
d:setuint32(offset, number) D: SetuInt32 () inserta un número de 32 bits sin firmar en la memoria a la que se hace referencia un objeto data y un offset de bytes, comenzando desde cero.
d:getint64(offset) D: getInt64 () extrae un entero de 64 bits firmado de la memoria a la que se hace referencia un objeto data y un offset de bytes, comenzando desde cero.
d:setint64(offset, number) D: SetInt64 () inserta un número firmado de 64 bits en la memoria referenciada por un objeto data y un offset de bytes, comenzando desde cero.
La biblioteca probe proporciona soporte para sondas de kernel.
probe.new(symbol|address, handlers) sonde.new () Devuelve un nuevo objeto probe para monitorear un symbol de núcleo (cadena) o address (Light UserData) e instala sus handlers en el sistema. El handler debe definirse como una tabla que contiene el siguiente campo:
pre : la función a llamar antes de la instrucción sondeada. Recibe el symbol o address , seguido de un cierre que se puede llamar para mostrar los registros de la CPU y la pila en el registro del sistema.post : función que se llamará después de la instrucción sondeada. Recibe el symbol o address , seguido de un cierre que se puede llamar para mostrar los registros de la CPU y la pila en el registro del sistema. p:stop() P: Stop () Elimina los controladores probe del sistema.
p:enable(bool) P: habilitar () habilita o deshabilita los manejadores probe , en consecuencia para bool .
La biblioteca syscall proporciona soporte para las direcciones y números de llamadas del sistema.
syscall.address(number) syscall.address () Devuelve la dirección de llamada del sistema (Light UserData) a la que se hace referencia el number dado.
syscall.number(name) syscall.number () Devuelve el número de llamada del sistema referenciado por el name de pila.
La biblioteca syscall.table proporciona soporte para traducir los nombres de las llamadas del sistema a direcciones (Light UserData).
La biblioteca xdp proporciona soporte para el subsistema de la ruta de datos del kernel express (XDP). Esta biblioteca se inspiró en el proyecto GSOC de Victor Nogueira.
xdp.attach(callback) xdp.attach () registra una función callback al runtime actual que se llama desde un programa XDP/EBPF cada vez que llame a BPF_LUAXDP_RUN KFUNC. Esta callback recibe los siguientes argumentos:
buffer : un objeto data que representa el búfer de red.argument : un objeto data que contiene el argumento aprobado por el programa XDP/EBPF. La función callback podría devolver los valores definidos por la tabla XDP.Action.
xdp.detach() XDP.DETACH () UNGISTRA La callback asociada con el runtime actual, si corresponde.
xdp.actionXDP.ACTION es una tabla que exporta los indicadores XDP_ACTION a LUA.
"ABORTED" : indica que el programa XDP abortó, generalmente debido a un error."DROP" : especifica que el paquete debe ser caído, descartándolo por completo."PASS" : permite que el paquete pase a la pila de red de Linux."TX" : transmite el paquete nuevamente en la misma interfaz que se recibió."REDIRECT" : redirige el paquete a otra interfaz o contexto de procesamiento. La biblioteca xtable brinda soporte para desarrollar extensiones NetFilter Xtable.
xtable.match(opts)Xtable.match () Devuelve un nuevo objeto XTable para extensiones de coincidencia. Esta función recibe los siguientes argumentos:
opts : una tabla que contiene los siguientes campos:name : cadena que representa el nombre de extensión XTable.revision : entero que representa la revisión de extensión XTable.family : Dirección de la familia, una de NetFilter. Familia.proto : número de protocolo, uno de Socket.ipproto.hooks : gancho para conectar la extensión a, un valor de cualquiera de la tabla de ganchos: netfilter.inet_hooks, netfilter.bridge_hooks y netfilter.arp_hooks (nota: netfilter.netdev_hooks no está disponible para Legacy X_Tables). (Por ejemplo, 1 << inet_hooks.LOCAL_OUT ).match : función que se solicitará para los paquetes coincidentes. Recibe los siguientes argumentos:skb (Readonly): un objeto data que representa el búfer de socket.par : una tabla que contiene campos hotdrop , thoff (desplazamiento del encabezado de transporte) y fragoff (compensación de fragmentos).userargs : una cadena LUA pasó desde el módulo UserSpace Xtable.true si el paquete coincide con la extensión; De lo contrario, debe devolver false .checkentry : se solicitará a la función para verificar la entrada. Esta función recibe userargs como su argumento.destroy : Función a ser convocada para destruir la extensión XTable. Esta función recibe userargs como su argumento. xtable.target(opts)Xtable.target () Devuelve un nuevo objeto XTable para la extensión de destino. Esta función recibe los siguientes argumentos:
opts : una tabla que contiene los siguientes campos:name : cadena que representa el nombre de extensión XTable.revision : entero que representa la revisión de extensión XTable.family : Dirección de la familia, una de NetFilter. Familia.proto : número de protocolo, uno de Socket.ipproto.hooks : gancho para conectar la extensión a, un valor de cualquiera de la tabla de ganchos: netfilter.inet_hooks, netfilter.bridge_hooks y netfilter.arp_hooks (nota: netfilter.netdev_hooks no está disponible para Legacy X_Tables). (Por ejemplo, 1 << inet_hooks.LOCAL_OUT ).target : Función que se solicitará para los paquetes de orientación. Recibe los siguientes argumentos:skb : un objeto data que representa el búfer de socket.par (Readonly): una tabla que contiene campos hotdrop , thoff (desplazamiento del encabezado de transporte) y fragoff (compensación de fragmentos).userargs : una cadena LUA pasó desde el módulo UserSpace Xtable.checkentry : se solicitará a la función para verificar la entrada. Esta función recibe userargs como su argumento.destroy : Función a ser convocada para destruir la extensión XTable. Esta función recibe userargs como su argumento. La biblioteca netfilter proporciona soporte para el nuevo sistema NetFilter Hook.
netfilter.register(ops) NetFilter.Register () registra un nuevo gancho NetFilter con la tabla ops dada. Esta función recibe los siguientes argumentos:
ops : una tabla que contiene los siguientes campos:pf : Familia de Protocol, uno de NetFilter. Familiahooknum : gancho para conectar el filtro a, un valor de cualquiera de la tabla de ganchos: netfilter.inet_hooks, netfilter.bridge_hooks, netfilter.arp_hooks y netfilter.netdev_hooks. (Por ejemplo, inet_hooks.LOCAL_OUT + 11 ).priority : Prioridad del gancho. Uno de los valores de NetFilter.IP_Priority o NetFilter.Bridge_Priority Tablas.hook : Función que se convoca para el gancho. Recibe los siguientes argumentos:skb : a data object representing the socket buffer.netfilter.familynetfilter.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.actionnetfilter.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_hooksnetfilter.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_hooksnetfilter.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_hooksnetfilter.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_hooksnetfilter.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_prioritynetfilter.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_prioritynetfilter.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 The luaxt userspace library provides support for generating userspace code for xtable extensions.
To build the library, the following steps are required:
usr/lib/xtable and create a libxt_<ext_name>.lua file.luaxt ) in the created file.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 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.familyhelp : 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 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.familyhelp : 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.familyluaxt.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.
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:
truenil, "timeout"nil, "interrupt" spyglass is a kernel script that implements a keylogger inspired by the 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, (eg, <del> stands for 127 ).
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 is a kernel script that implements 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.
sudo make examples_install # installs examples
sudo lunatik run examples/keylocker # runs keylocker
<↑> <↑> <↓> <↓> <←> <→> <←> <→> <LCTRL> <LALT> # locks keyboard
<↑> <↑> <↓> <↓> <←> <→> <←> <→> <LCTRL> <LALT> # unlocks keyboard
tap 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.
sudo make examples_install # installs examples
sudo lunatik run examples/tap # runs tap
cat /dev/tap
shared is a kernel script that implements an in-memory key-value store using rcu, data, socket and thread.
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 is an echo server implemented as kernel scripts.
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 is a kernel script that implements a device driver to monitor system calls. It prints the amount of times each system call was called since the driver has been installed.
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 is a kernel extension composed by a XDP/eBPF program to filter HTTPS sessions and a Lua kernel script to filter SNI TLS extension. This kernel extension drops any HTTPS request destinated to a blacklisted server.
Compile and install libbpf , libxdp and xdp-loader :
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 installCome back to this repository, install and load the filter:
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 programFor example, testing is easy thanks to docker. Assuming docker is installed and running:
sudo xdp-loader load -m skb docker0 https.o
sudo journalctl -ft kerneldocker 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 .
This other sni filter uses netfilter api.
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.
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
sudo make examples_install # installs examples
sudo lunatik run examples/dnsblock/nf_dnsblock false # runs the Lua kernel script
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.
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
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
Lunatik is dual-licensed under MIT or GPL-2.0-only.
Lua submodule is licensed under MIT. For more details, see its Copyright Notice.
Klibc submodule is dual-licensed under BSD 3-Clause or GPL-2.0-only. For more details, see its LICENCE file.