Lunatik é uma estrutura para roteirizar o kernel Linux com Lua. É composto pelo intérprete Lua modificado para ser executado no kernel; um driver de dispositivo (escrito em lua =)) e uma ferramenta de linha de comando para carregar e executar scripts e gerenciar ambientes de tempo de execução no espaço do usuário; uma API C para carregar e executar scripts e gerenciar ambientes de tempo de execução do kernel; e Lua APIs para vincular instalações do kernel aos scripts da Lua.
Aqui está um exemplo de um driver de dispositivo de caracteres escrito em Lua usando Lunatik para gerar caracteres imprimíveis ASCII aleatórios:
-- /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 )Instale dependências (aqui para o Debian/Ubuntu, a ser adaptado à distribuição de alguém):
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 Uma vez feito, o script de debian_kernel_postinst_lunatik.sh da Tools/pode ser copiado em /etc/kernel/postinst.d/ : isso garante que lunatik (e também o xdp necessário LIBs) seja compilado no upgrade do 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 : Carregar módulos do kernel lunatikunload : descarregar módulos de kernel lunatikreload : Recarregar os módulos do kernel lunatikstatus : mostre quais módulos de kernel lunatik estão atualmente carregadoslist : mostre quais ambientes de tempo de execução estão sendo executados no momentorun : Crie um novo ambiente de tempo de execução para executar o script /lib/modules/lua/<script>.luaspawn : Crie um novo ambiente de tempo de execução e gere um tópico para executar o script /lib/modules/lua/<script>.luastop : Pare o ambiente de tempo de execução criado para executar o script <script>default : Inicie um repl (leitura - loop de impressão de eval) O Lunatik 3.4 é baseado no Lua 5.4 adaptado para correr no kernel.
O LUNATIK não suporta aritmética de ponto flutuante, portanto, não suporta __div nem metames __pow e o número do tipo possui apenas o número inteiro do subtipo.
Lunatik não suporta bibliotecas de IO e SO e os identificadores dados das seguintes bibliotecas:
Lunatik modifica os seguintes identificadores:
"Lua 5.4-kernel" ."/lib/modules/lua/?.lua;/lib/modules/lua/?/init.lua" .Lunatik não suporta Lual_stream, Lual_execResult, Lual_fileResult, luaopen_io e luaopen_os.
Lunatik modifica lual_openlibs para remover Luaopen_IO e Luaopen_OS.
#include <lunatik.h> int lunatik_runtime ( lunatik_object_t * * pruntime , const char * script , bool sleep ); lunatik_runtime () cria um novo ambiente runtime e carrega e executa o script /lib/modules/lua/<script>.lua como ponto de entrada para esse ambiente. Ele só deve ser chamado do contexto do processo . O ambiente runtime é um objeto Lunatik que detém um estado da Lua. Os objetos lunatik são os dados especiais do Lua UserData, que também contêm um tipo de bloqueio e um contador de referência. Se sleep for verdadeiro , Lunatik_runtime () usará um mutex para bloquear o ambiente runtime e o sinalizador GFP_KERNEL para alocar a nova memória posteriormente nas chamadas lunatik_run (). Caso contrário, ele usará um spinlock e gfp_atomic. lunatik_runtime () abre as bibliotecas padrão da Lua presentes no Lunatik. Se for bem -sucedido, lunatik_runtime () define o endereço apontado pelo espaço extra de pruntime e Lua com um ponteiro para o novo ambiente de runtime criado, define o contador de referência para 1 e depois retorna 0 . Caso contrário, ele retorna -ENOMEM , se a memória insuficiente estiver disponível; ou -EINVAL , se não conseguir carregar ou executar o 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 () fecha o estado da lua criado para esse ambiente runtime e diminui o contador de referência. Depois que o contador de referência é diminuído a zero, o tipo de bloqueio e a memória alocada para o ambiente runtime são liberados. Se o ambiente runtime foi lançado, ele retornará 1 ; Caso contrário, ele retorna 0 .
void lunatik_run ( lunatik_object_t * runtime , < inttype > ( * handler )(...), < inttype > & ret , ...); lunatik_run () bloqueia o ambiente runtime e chama o handler que passa pelo estado da Lua associado como o primeiro argumento seguido pelos argumentos variáveis. Se o estado de Lua foi fechado, ret será definido com -ENXIO ; Caso contrário, ret está definido com o resultado da chamada handler(L, ...) . Em seguida, ele restaura a pilha Lua e desbloqueia o ambiente runtime . É definido como uma 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 o contador de referência desse object (por exemplo, ambiente runtime ).
int lunatik_putobject ( lunatik_object_t * object ); lunatik_putObject () diminui o contador de referência desse object (por exemplo, ambiente runtime ). Se o object foi liberado, ele retornará 1 ; Caso contrário, ele retorna 0 .
lunatik_object_t * lunatik_toruntime ( lua_State * L ); lunatik_toruntime () retorna o ambiente runtime referenciado pelo espaço extra do L
A biblioteca lunatik fornece suporte para carregar e executar scripts e gerenciar ambientes de tempo de execução da Lua.
lunatik.runtime(script [, sleep]) lunatik.runtime () cria um novo ambiente de tempo de execução e carrega e executa o script /lib/modules/lua/<script>.lua como ponto de entrada para esse ambiente. Ele retorna um objeto lunatik representando o ambiente runtime . Se sleep for verdadeiro ou omitido, ele usará um mutex e gfp_kernel; Caso contrário, ele usará um spinlock e gfp_atomic. lunatik.runtime () abre as bibliotecas padrão da Lua presentes no Lunatik.
runtime:stop() Tempo de execução: stop () interrompe o ambiente runtime e limpe sua referência do objeto de tempo de execução.
runtime:resume([obj1, ...]) Tempo de execução: retume () retoma a execução de um runtime . Os valores obj1, ... são passados como os argumentos para a função retornada na criação runtime . Se o runtime rendeu, o resume() reiniciar; Os valores obj1, ... são passados como os resultados do rendimento.
A biblioteca device fornece suporte para escrever drivers de dispositivos de caracteres em Lua.
device.new(driver) Device.New () retorna um novo objeto device e instala seu driver no sistema. O driver deve ser definido como uma tabela que contém o seguinte campo:
name : String definindo o nome do dispositivo; É usado para criar o arquivo de dispositivo (por exemplo, /dev/<name> ). A tabela driver pode opcionalmente conter os seguintes campos:
read : Função de retorno de chamada para lidar com a operação de leitura no arquivo do dispositivo. Ele recebe a tabela driver como o primeiro argumento seguido por dois números inteiros, o length a ser lido e o offset do arquivo. Ele deve retornar uma string e, opcionalmente, o updated offset . Se o comprimento da sequência retornado for maior que o length solicitado, a string será corrigida para esse length . Se o updated offset não for retornado, o offset será atualizado com offset + length .write : Função de retorno de chamada para lidar com a operação de gravação no arquivo do dispositivo. Ele recebe a tabela driver como o primeiro argumento seguido pela string a ser escrito e um número inteiro como offset do arquivo. Pode retornar opcionalmente, o length escrito seguido pelo updated offset . Se o comprimento retornado for maior que o length solicitado, o comprimento retornado será corrigido. Se o updated offset não for retornado, o offset será atualizado com offset + length .open : Função de retorno de chamada para lidar com a operação aberta no arquivo do dispositivo. Ele recebe a tabela driver e espera -se que não devolva nada.release : Função de retorno de chamada para lidar com a operação de liberação no arquivo do dispositivo. Ele recebe a tabela driver e espera -se que não devolva nada.mode : um número inteiro especificando o modo de arquivo do dispositivo. Se um retorno de chamada de operação não estiver definido, o device retornará -ENXIO ao VFS em seu acesso.
device.stop(dev) , dev:stop() Device.stop () remove um driver de dispositivo especificado pelo objeto dev do sistema.
A biblioteca linux fornece suporte para algumas instalações do kernel Linux.
linux.random([m [, n]])Linux.Random () imita o comportamento de Math.random, mas Binding <Linux/Random.h > 's get_random_u32 () e get_random_u64 () APIs.
Quando chamado sem argumentos, produz um número inteiro com todos os bits (pseudo) aleatórios. Quando chamado com dois números inteiros m e n , Linux.Random () retorna um número inteiro pseudo-aleatório com distribuição uniforme na faixa [m, n] . A chamada math.random(n) , para um n positivo, é equivalente a math.random(1, n) .
linux.statLinux.stat é uma tabela que exporta sinalizadores <Linux/stat.h> inteiro para Lua.
"IRWXUGO" : permissão para ler , escrever e executar para usuário , grupo e outros ."IRUGO" : permissão apenas para ler para usuário , grupo e outros ."IWUGO" : permissão apenas para escrever para usuário , grupo e outros ."IXUGO" : permissão apenas para executar o usuário , o grupo e outros . linux.schedule([timeout [, state]]) Linux.Schedule () define o state atual da tarefa e faz com que a TI durma até que os milissegundos timeout tenham decorrido. Se timeout for omitido, ele usa MAX_SCHEDULE_TIMEOUT . Se state for omitido, ele usa task.INTERRUPTIBLE .
linux.taskLinux.Task é uma tabela que exporta sinalizadores de estado da tarefa para Lua.
"RUNNING" : Task está executando em uma CPU ou esperando para ser executado."INTERRUPTIBLE" : a tarefa está aguardando um sinal ou um recurso (dormindo)."UNINTERRUPTIBLE" : se comporta como "interrompida" com a exceção de que o sinal não acordará a tarefa."KILLABLE" : se comporta como "ininterrupto" com a exceção de que os sinais fatais acordarão a tarefa."IDLE" : se comporta como "ininterrupto" com a exceção de evitar a contabilidade do loadavg. linux.time()Linux.Time () retorna o horário atual em nanossegundos desde a época.
linux.errnoLinux.errno é uma tabela que exporta <uapi/asm-generic/errno-Base.h> sinalizadores para lua.
"PERM" : operação não permitida."NOENT" : nenhum arquivo ou diretório."SRCH" : não é esse processo."INTR" : Chamada de sistema interrompido."IO" : Erro de E/S."NXIO" : nenhum dispositivo ou endereço."2BIG" :, Lista de argumentos por muito tempo."NOEXEC" : Erro de formato EXEC."BADF" : número de arquivo ruim."CHILD" : sem processos infantis."AGAIN" : tente novamente."NOMEM" : fora da memória."ACCES" : permissão negada."FAULT" : endereço ruim."NOTBLK" : dispositivo de bloco necessário."BUSY" : dispositivo ou recurso ocupado."EXIST" : existe o arquivo."XDEV" : link de dispositivo cruzado."NODEV" : nenhum dispositivo."NOTDIR" : não é um diretório."ISDIR" : é um diretório."INVAL" : argumento inválido."NFILE" : Overflow da tabela de arquivos."MFILE" : muitos arquivos abertos."NOTTY" : não é uma máquina de escrever."TXTBSY" : arquivo de texto ocupado."FBIG" : arquivo muito grande."NOSPC" : nenhum espaço deixou no dispositivo."SPIPE" : busca ilegal."ROFS" : sistema de arquivos somente leitura."MLINK" : muitos links."PIPE" : tubo quebrado."DOM" : argumento matemático fora do domínio do func."RANGE" : resultado matemático não representável. linux.hton16(num)Linux.hton16 () converte o pedido de byte host em ordem de byte de rede para um número inteiro de 16 bits.
linux.hton32(num)Linux.hton32 () converte o pedido de byte do host em ordem de byte de rede para um número inteiro de 32 bits.
linux.hton64(num)Linux.hton64 () converte o pedido de byte host em ordem de byte de rede para um número inteiro de 64 bits.
linux.ntoh16(num)Linux.noh16 () converte a ordem de byte de rede para hospedar pedidos de byte para um número inteiro de 16 bits.
linux.ntoh32(num)Linux.noh32 () converte a ordem de byte de rede para hospedar pedidos de byte para um número inteiro de 32 bits.
linux.ntoh64(num)Linux.noh64 () converte a ordem de byte de rede para hospedar pedidos de byte para um número inteiro de 64 bits.
linux.htobe16(num)linux.htobe16 () converte a ordem do byte host em ordem de byte big-endian para um número inteiro de 16 bits.
linux.htobe32(num)linux.htobe32 () converte a ordem do byte host em ordem de byte big-endian para um número inteiro de 32 bits.
linux.htobe64(num)linux.htobe64 () converte a ordem do byte host em ordem de byte big-endian para um número inteiro de 64 bits.
linux.be16toh(num)Linux.be16toh () converte a ordem de byte big-endian para hospedar ordem de bytes para um número inteiro de 16 bits.
linux.be32toh(num)Linux.be32toh () converte a ordem de byte big-endian para hospedar pedidos de byte para um número inteiro de 32 bits.
linux.be64toh(num)Linux.be64toh () converte a ordem de byte big-endian para hospedar pedidos de byte para um número inteiro de 64 bits.
linux.htole16(num)Linux.htole16 () converte a ordem do byte do host em ordem de byte pouco endiana para um número inteiro de 16 bits.
linux.htole32(num)linux.htole32 () converte a ordem do byte host em ordem de byte pouco endiana para um número inteiro de 32 bits.
linux.htole64(num)Linux.htole64 () converte a ordem do byte do host em ordem de byte pouco endiana para um número inteiro de 64 bits.
linux.le16toh(num)Linux.le16toh () converte a ordem de byte pouco endiana para hospedar pedidos de byte para um número inteiro de 16 bits.
linux.le32toh(num)Linux.le32toh () converte a ordem de byte pouco endiana para hospedar pedidos de byte para um número inteiro de 32 bits.
linux.le64toh(num)Linux.le64toh () converte a ordem de byte pouco endiana para hospedar pedidos de byte para um número inteiro de 64 bits.
A biblioteca notifier fornece suporte para as cadeias notificadoras do kernel.
notifier.keyboard(callback) notifier.Keyboard () retorna um novo objeto notifier de teclado e o instala no sistema. A função callback é chamada sempre que um evento de teclado do console acontece (por exemplo, uma tecla foi pressionada ou liberada). Este callback recebe os seguintes argumentos:
event : Os eventos disponíveis são definidos pela tabela Notifier.kbd.down : true , se a tecla for pressionada; false , se for lançado.shift : true , se a chave de mudança for mantida; false , caso contrário.key : Código de KeyCode ou Keysym, dependendo do event . A função callback pode retornar os valores definidos pela tabela Notifier.Notify.
notifier.kbdnotifier.kbd é uma tabela que exporta sinalizadores KBD para Lua.
"KEYCODE" : Keyboard KeyCode , chamado antes de qualquer outro."UNBOUND_KEYCODE" : Código de chave de teclado que não está vinculado a nenhum outro."UNICODE" : Unicode do teclado."KEYSYM" : teclado Keysym ."POST_KEYSYM" : chamado após a interpretação do teclado do teclado. notifier.netdevice(callback) notifier.netDevice () Retorna um novo objeto NetDevice notifier e o instala no sistema. A função callback é chamada sempre que um evento de console NetDevice ocorre (por exemplo, uma interface de rede foi conectada ou desconectada). Este callback recebe os seguintes argumentos:
event : Os eventos disponíveis são definidos pela tabela notificer.netdev.name : o nome do dispositivo. A função callback pode retornar os valores definidos pela tabela Notifier.Notify.
notifier.netdevnotifier.netDev é uma tabela que exporta sinalizadores NetDev para Lua.
notifier.notifynotifier.Notify é uma tabela que exporta Notificar sinalizadores para Lua.
"DONE" : não se importe."OK" : me convém."BAD" : ação ruim/veto."STOP" : limpe a maneira de retornar do notificador e interromper mais chamadas. notfr:delete() Notfr: delete () remove um notifier especificado pelo objeto notfr do sistema.
A biblioteca socket fornece suporte para o manuseio de redes do kernel. Esta biblioteca foi inspirada no projeto GSOC de Chengzhi Tan.
socket.new(family, type, protocol) Socket.New () cria um novo objeto socket . Esta função recebe os seguintes argumentos:
family : o endereço disponível as famílias são definidas pela tabela Socket.Af.sock : os tipos disponíveis estão presentes na tabela de soquete.protocol : os protocolos disponíveis são definidos pela tabela Socket.ipproto. socket.afSocket.Af é uma tabela que exporta as famílias (AF) para Lua.
"UNSPEC" : não especificado."UNIX" : soquetes de domínio Unix."LOCAL" : Nome POSIX para AF_UNIX."INET" : Protocolo IP da Internet."AX25" : AX RÁDIO AMADOR.25."IPX" : Novell IPX."APPLETALK" : AppleTalk DDP."NETROM" : rede de rádio amador/ROM."BRIDGE" : ponte multiprotocolo."ATMPVC" : PVCs ATM."X25" : Reservado para o projeto X.25."INET6" : IP Versão 6."ROSE" : Rádio Amador X.25 PLP."DEC" : Reservado para o Projeto Decnet."NETBEUI" : reservado para o projeto 802.2LLC."SECURITY" : pseudo -chamada de segurança AF."KEY" : API de gerenciamento de chaves pf_key."NETLINK" : NetLink."ROUTE" : Alias para emular 4.4BSD."PACKET" : família de pacotes."ASH" : Ash."ECONET" : ECONET DE ACORN."ATMSVC" : atm SVCs."RDS" : Sockets RDS."SNA" : Linux SNA Project (Nutters!)."IRDA" : soquetes Irda."PPPOX" : soquetes ppox."WANPIPE" : Wanpipe API Sockets."LLC" : Linux LLC."IB" : endereço infiniband nativo."MPLS" : MPLS."CAN" : Rede de Área do Controlador."TIPC" : soquetes TIPC."BLUETOOTH" : soquetes Bluetooth."IUCV" : soquetes IUCV."RXRPC" : soquetes rxrpc."ISDN" : MISDN Sockets."PHONET" : soquetes foneteos."IEEE802154" : IEEE802154 SOCKETS."CAIF" : soquetes CAIF."ALG" : soquetes de algoritmo."NFC" : soquetes NFC."VSOCK" : vsockets."KCM" : multiplexor de conexão do kernel."QIPCRTR" : Roteador da Qualcomm IPC."SMC" : Número de reserva para a família PF_SMC Protocol que reutiliza a família AF_INET Endereço."XDP" : soquetes XDP."MCTP" : Protocolo de Transporte de Componentes de Gerenciamento."MAX" : máximo. socket.sockSocket.sock é uma tabela que exporta tipos de soquete (meia):
"STREAM" : STRAME (conexão) soquete."DGRAM" : soquete de datagrama (conexão)."RAW" : soquete bruto."RDM" : mensagem entregue de maneira confiável."SEQPACKET" : soquete de pacote seqüencial."DCCP" : soquete do protocolo de controle de congestionamento do datagrama."PACKET" : maneira específica do Linux de obter pacotes no nível do desenvolvimento.e bandeiras (meia):
"CLOEXEC" : n/a."NONBLOCK" : n/a. socket.ipprotoSocket.ipproto é uma tabela que exporta protocolos IP (ipproto) para Lua.
"IP" : protocolo fictício para TCP."ICMP" : protocolo de mensagem de controle da Internet."IGMP" : Protocolo de gerenciamento de grupos da Internet."IPIP" : túneis IPIP (túneis Ka9Q mais antigos usam 94)."TCP" : protocolo de controle de transmissão."EGP" : Protocolo de Gateway Exterior."PUP" : protocolo de filhote."UDP" : protocolo de datagrama do usuário."IDP" : protocolo XNS IDP."TP" : então Transport Protocol Class 4."DCCP" : protocolo de controle de congestionamento de datagrama."IPV6" : Tunelamento IPv6-in-IPV4."RSVP" : protocolo RSVP."GRE" : túneis Cisco Gre (RFC 1701.1702)."ESP" : Protocolo de carga de segurança de segurança de encapsulamento."AH" : Protocolo de cabeçalho de autenticação."MTP" : protocolo de transporte multicast."BEETPH" : opção IP Pseudo -cabeçalho para beterraba."ENCAP" : cabeçalho de encapsulamento."PIM" : multicast independente do protocolo."COMP" : protocolo de cabeçalho de compressão."SCTP" : protocolo de transporte de controle de fluxo."UDPLITE" : UDP-Lite (RFC 3828)."MPLS" : MPLS em IP (RFC 4023)."ETHERNET" : encapsulamento Ethernet-Within-IPV6."RAW" : pacotes IP brutos."MPTCP" : conexão multipath TCP. sock:close() Soca: Close () remove o objeto sock do sistema.
sock:send(message, [addr [, port]]) Soca: send () envia uma message de string através do sock . Se a família de endereços sock for af.INET , espera os seguintes argumentos:
addr : integer descrevendo o endereço IPv4 de destino.port : integer descrevendo a porta IPv4 de destino.De outra forma:
addr : string embalada descrevendo o endereço de destino. sock:receive(length, [flags [, from]]) Meia: Receber () recebe uma string com bytes de até length através da sock de soquete. Os sinalizadores de mensagens disponíveis são definidos pela tabela Socket.MSG. Se from true , ele retorna a mensagem recebida seguida pelo endereço do par. Caso contrário, ele retorna apenas a mensagem recebida.
socket.msgSocket.MSG é uma tabela que exporta sinalizadores de mensagens para Lua.
"OOB" : n/a."PEEK" : n/a."DONTROUTE" : n/a."TRYHARD" : sinônimo de "DONTROUTE" para Decnet."CTRUNC" : n/a."PROBE" : não envie. Somente o caminho da sonda Fe para MTU."TRUNC" : n/a."DONTWAIT" : io não bloqueado."EOR" : Fim do registro."WAITALL" : aguarde um pedido completo."FIN" : n/a."SYN" : n/a."CONFIRM" : confirme a validade do caminho."RST" : n/a."ERRQUEUE" : busca mensagem da fila de erros."NOSIGNAL" : não gera sigpipe."MORE" : o remetente enviará mais."WAITFORONE" : recvmmsg (): bloqueie até 1+ pacotes aproveitados."SENDPAGE_NOPOLICY" : sendpage () interno: não aplique política."SENDPAGE_NOTLAST" : sendpage () interno: não é a última página."BATCH" : sendmmsg (): mais mensagens chegando."EOF" : n/a."NO_SHARED_FRAGS" : sendpage () interno: os frags de página não são compartilhados."SENDPAGE_DECRYPTED" : sendpage () interno: a página pode transportar texto simples e exigir criptografia."ZEROCOPY" : use dados do usuário no caminho do kernel."FASTOPEN" : envie dados no TCP Syn."CMSG_CLOEXEC" : set Close_on_Exec para o descritor de arquivo recebido através do SCM_RIGHTS. sock:bind(addr [, port]) Meia: Bind () liga a sock de soquete a um determinado endereço. Se a família de endereços sock for af.INET , espera os seguintes argumentos:
addr : integer descrevendo o endereço IPv4 do host.port : integer descrevendo a porta IPv4 do host.De outra forma:
addr : string compactada descrevendo o endereço do host. sock:listen([backlog]) Sock: Ouça () move o sock para o estado de escuta.
backlog : Tamanho da fila de conexões pendentes. Se omitido, ele usa SomaxConn como padrão. sock:accept([flags]) Meia: aceita () aceita uma conexão no sock . Ele retorna um novo objeto socket . Os sinalizadores disponíveis estão presentes na tabela de soquete.
sock:connect(addr [, port] [, flags]) Meia: Connect () conecta o sock ao addr . Se a família de endereços sock for af.INET , espera os seguintes argumentos:
addr : integer descrevendo o endereço IPv4 de destino.port : integer descrevendo a porta IPv4 de destino.De outra forma:
addr : string embalada descrevendo o endereço de destino.Os sinalizadores disponíveis estão presentes na tabela de soquete.
Para soquetes de datagrama, addr é o endereço para o qual os datagramas são enviados por padrão e o único endereço do qual os datagramas são recebidos. Para soquetes de fluxo, as tentativas de conectar ao addr .
sock:getsockname() Meia: getsockname () Obtenha o endereço que o sock está vinculado. Se a família de endereços sock estiver af.INET , ele retornará o seguinte:
addr : integer descrevendo o endereço IPv4 limitado.port : integer descrevendo a porta IPv4 limitada.De outra forma:
addr : string embalada descrevendo o endereço limitado. sock:getpeername() Meia: getPeerName () Obtenha o endereço que o sock está conectado. Se a família de endereços sock estiver af.INET , ele retornará o seguinte:
addr : integer descrevendo o endereço IPv4 do par.port : integer descrevendo a porta IPv4 do par.De outra forma:
addr : string embalada descrevendo o endereço do par. A biblioteca socket.inet fornece suporte para soquetes IPv4 de alto nível.
inet.tcp() O INET.TCP () cria um novo socket usando a família de endereços AF.INET, Sock.Stream Type e Iproto.tcp Protocol. Ele substitui os métodos socket para usar endereços como notação de números e pontos (por exemplo, "127.0.0.1" ), em vez de números inteiros.
inet.udp() O INT.UDP () cria um novo socket usando a família de endereços AF.INET, SOCK.Dgram Type e Iproto.udp Protocol. Ele substitui os métodos socket para usar endereços como notação de números e pontos (por exemplo, "127.0.0.1" ), em vez de números inteiros.
udp:receivefrom(length [, flags]) UDP: REPETFROM () é apenas um alias para a sock:receive(length, flags, true) .
A biblioteca rcu fornece suporte para o mecanismo de sincronização do Kernel Read-Copy Atualize (RCU). Esta biblioteca foi inspirada no projeto GSOC da Caio Messias.
rcu.table([size]) rcu.table () cria um novo objeto rcu.table que liga a tabela de hash genéricos do kernel. Essa função recebe como argumento o número de baldes arredondados para a próxima potência de 2. O tamanho padrão é 1024 . A chave deve ser uma string e o valor deve ser um objeto lunatik ou nulo.
A biblioteca thread fornece suporte para as primitivas do Thread Kernel.
thread.run(runtime, name) Thread.run () cria um novo objeto thread e acorda. Esta função recebe os seguintes argumentos:
runtime : o ambiente de tempo de execução para executar uma tarefa no thread do kernel criado. A tarefa deve ser especificada retornando uma função no script carregado no ambiente runtime .name : String representando o nome do thread (por exemplo, como mostrado no ps ). thread.shouldstop() thread.shouldstop () retorna true se thread.stop () foi chamado; Caso contrário, ele retorna false .
thread.current() thread.current () retorna um objeto thread que representa a tarefa atual.
thrd:stop() thrd: stop () define thread.swouldstop () no thread thrd para retornar True, acorda thrd e aguarda a saída.
thrd:task() THRD: Task () retorna uma tabela que contém as informações da tarefa deste thread (por exemplo, "CPU", "Command", "PID" e "TGID").
A biblioteca fib fornece suporte para a base de informações de encaminhamento do kernel.
fib.newrule(table, priority)fib.NewRule () liga a API do kernel fib_nl_newrule; Ele cria uma nova regra do FIB que corresponde à tabela de roteamento especificada com a priorioty especificada. Esta função é semelhante à regra IP do comando do espaço do usuário, add fornecido pelo iProute2.
fib.delrule(table, priority)fib.delrule () liga a API do kernel fib_nl_delrule; Ele remove uma regra FIB que corresponde à tabela de roteamento especificada com a priorioty especificada. Esta função é semelhante à regra IP do comando do espaço do usuário fornecida pelo iProute2.
A biblioteca data fornece suporte para vincular a memória do sistema à Lua.
data.new(size) Data.New () cria um novo objeto data que aloca bytes size .
d:getnumber(offset) D: GetNumber () extrai um lua_integer da memória referenciada por um objeto data e um offset de byte, a partir de zero.
d:setnumber(offset, number) D: SetNumber () Insira um number Lua_Integer na memória referenciada por um objeto data e um offset de byte, a partir de zero.
d:getbyte(offset) D: GetByte () extrai um byte da memória referenciada por um objeto data e um offset de byte, a partir de zero.
d:setbyte(offset, byte) D: Setbyte () Insira um byte na memória referenciada por um objeto data e um offset de byte, a partir de zero.
d:getstring(offset[, length]) D: GetTring () extrai uma string com bytes length da memória referenciada por um objeto data e um offset de byte, a partir de zero. Se length for omitido, ele extrai todos os bytes do offset até o final dos data .
d:setstring(offset, s) D: SetString () Insira as s na memória referenciada por um objeto data e um offset de byte, começando de zero.
d:getint8(offset) D: GetInt8 (D, deslocamento) extrai um número inteiro de 8 bits assinado da memória referenciada por um objeto data e um offset de bytes, começando de zero.
d:setint8(offset, number) D: SetInt8 () insere um número de 8 bits assinado na memória referenciada por um objeto data e um offset de byte, a partir de zero.
d:getuint8(offset) D: getUint8 () extrai um número inteiro de 8 bits não assinado da memória referenciada por um objeto data e um offset de bytes, começando de zero.
d:setuint8(offset, number) D: Setuint8 () insere um número de 8 bits não assinado na memória referenciada por um objeto data e um offset de bytes, a partir de zero.
d:getint16(offset) D: GetInt16 () extrai um número inteiro de 16 bits assinado da memória referenciada por um objeto data e um offset de bytes, começando de zero.
d:setint16(offset, number) D: SetInt16 () insere um número de 16 bits assinado na memória referenciada por um objeto data e um offset de byte, a partir de zero.
d:getuint16(offset) D: GetUint16 () extrai um número inteiro de 16 bits não assinado da memória referenciada por um objeto data e um offset de byte, começando de zero.
d:setuint16(offset, number) D: SetuInt16 () insere um número de 16 bits não assinado na memória referenciada por um objeto data e um offset de byte, começando de zero.
d:getint32(offset) D: GetInt32 () extrai um número inteiro assinado de 32 bits da memória referenciada por um objeto data e um offset de bytes, começando de zero.
d:setint32(offset, number) D: SetInt32 () insere um número de 32 bits assinado na memória referenciada por um objeto data e um offset de bytes, a partir de zero.
d:getuint32(offset) D: GetUINT32 () extrai um número inteiro de 32 bits não assinado da memória referenciada por um objeto data e um offset de byte, a partir de zero.
d:setuint32(offset, number) D: SetuInt32 () insere um número de 32 bits não assinado na memória referenciada por um objeto data e um offset de byte, a partir de zero.
d:getint64(offset) D: GetInt64 () extrai um número inteiro assinado de 64 bits da memória referenciada por um objeto data e um offset de bytes, começando de zero.
d:setint64(offset, number) D: SetInt64 () insere um número de 64 bits assinado na memória referenciada por um objeto data e um offset de bytes, a partir de zero.
A Biblioteca probe fornece suporte para sondas do kernel.
probe.new(symbol|address, handlers) Probe.New () retorna um novo objeto probe para monitorar um symbol do kernel (string) ou address (Data Data Data) e instala seus handlers no sistema. O handler deve ser definido como uma tabela que contém o seguinte campo:
pre : função a ser chamada antes da instrução sondada. Ele recebe o symbol ou address , seguido de um fechamento que pode ser chamado para mostrar os registros da CPU e a pilha no log do sistema.post : função a ser chamada após a instrução sondada. Ele recebe o symbol ou address , seguido de um fechamento que pode ser chamado para mostrar os registros da CPU e a pilha no log do sistema. p:stop() P: STOP () remove os manipuladores probe do sistema.
p:enable(bool) P: Ativar () Ativa ou desativa os manipuladores probe , de acordo com o bool .
A Biblioteca syscall fornece suporte para endereços e números de chamadas do sistema.
syscall.address(number) syscall.address () retorna o endereço de chamada do sistema (Light UserData) referenciado pelo number fornecido.
syscall.number(name) syscall.number () retorna o número de chamada do sistema referenciado pelo name fornecido.
A biblioteca syscall.table fornece suporte para a tradução de nomes de chamadas do sistema em endereços (Light UserData).
A biblioteca xdp fornece suporte para o subsistema Kernel Express Data Path (XDP). Esta biblioteca foi inspirada no projeto GSOC de Victor Nogueira.
xdp.attach(callback) xdp.attach () registra uma função callback no runtime atual para ser chamado de um programa XDP/EBPF sempre que chamar BPF_LUAXDP_RUN KFUNC. Este callback recebe os seguintes argumentos:
buffer : um objeto data que representa o buffer de rede.argument : Um objeto data que contém o argumento aprovado pelo programa XDP/EBPF. A função callback pode retornar os valores definidos pela tabela XDP.Action.
xdp.detach() XDP.Detach () não registra o callback associado ao runtime atual, se houver.
xdp.actionXDP.Action é uma tabela que exporta sinalizadores XDP_Action para Lua.
"ABORTED" : indica que o programa XDP abortou, normalmente devido a um erro."DROP" : especifica que o pacote deve ser descartado, descartando -o completamente."PASS" : permite que o pacote passe para a pilha de rede Linux."TX" : transmite o pacote de volta na mesma interface em que foi recebido."REDIRECT" : redireciona o pacote para outra interface ou contexto de processamento. A Biblioteca xtable fornece suporte para o desenvolvimento de extensões de netfilter xtable.
xtable.match(opts)xtable.match () Retorna um novo objeto xtable para extensões de correspondência. Esta função recebe os seguintes argumentos:
opts : Uma tabela que contém os seguintes campos:name : String representando o nome Xtable Extension.revision : Inteiro representando a revisão Xtable Extension.family : Endereço para a família, um dos netfilter.family.proto : Número do protocolo, um de soquete.ipproto.hooks : O gancho para anexar a extensão a, um valor de qualquer tabela de ganchos - netfilter.inet_hooks, netfilter.bridge_hooks e netfilter.arp_hooks (Nota: netfilter.netdev_hooks não está disponível para legado x_tables). (Por exemplo - 1 << inet_hooks.LOCAL_OUT ).match : função a ser chamada para pacotes correspondentes. Ele recebe os seguintes argumentos:skb (readonly): um objeto data que representa o buffer de soquete.par : Uma tabela contendo campos hotdrop , thoff (deslocamento do cabeçalho de transporte) e fragoff (deslocamento do fragmento).userargs : uma string lua passada do módulo Xtable do UsuáriosPace.true se o pacote corresponder à extensão; Caso contrário, ele deve retornar false .checkentry : função a ser solicitada para verificar a entrada. Esta função recebe userargs como seu argumento.destroy : função a ser chamado por destruir a extensão xtable. Esta função recebe userargs como seu argumento. xtable.target(opts)xtable.target () retorna um novo objeto Xtable para a extensão do destino. Esta função recebe os seguintes argumentos:
opts : Uma tabela que contém os seguintes campos:name : String representando o nome Xtable Extension.revision : Inteiro representando a revisão Xtable Extension.family : Endereço para a família, um dos netfilter.family.proto : Número do protocolo, um de soquete.ipproto.hooks : O gancho para anexar a extensão a, um valor de qualquer tabela de ganchos - netfilter.inet_hooks, netfilter.bridge_hooks e netfilter.arp_hooks (Nota: netfilter.netdev_hooks não está disponível para legado x_tables). (Por exemplo - 1 << inet_hooks.LOCAL_OUT ).target : Funcionar a ser chamado para pacotes de segmentação. Ele recebe os seguintes argumentos:skb : Um objeto data que representa o buffer de soquete.par (READONLY): Uma tabela que contém campos hotdrop , thoff (deslocamento do cabeçalho de transporte) e fragoff (Offset Fragment).userargs : uma string lua passada do módulo Xtable do UsuáriosPace.checkentry : função a ser solicitada para verificar a entrada. Esta função recebe userargs como seu argumento.destroy : função a ser chamado por destruir a extensão xtable. Esta função recebe userargs como seu argumento. A biblioteca netfilter fornece suporte para o novo sistema de gancho Netfilter.
netfilter.register(ops) netfilter.register () registra um novo gancho Netfilter na tabela ops fornecidas. Esta função recebe os seguintes argumentos:
ops : Uma tabela que contém os seguintes campos:pf : Família de Protocolo, um dos Netfilter.Familyhooknum : gancho para conectar o filtro a um valor de qualquer tabela de ganchos - netfilter.inet_hooks, netfilter.bridge_hooks, netfilter.arp_hooks e netfilter.netdev_hooks. (Por exemplo - inet_hooks.LOCAL_OUT + 11 ).priority : prioridade do gancho. Um dos valores das tabelas netfilter.ip_priority ou netfilter.bridge_priority.hook : Funcionar a ser chamado para o gancho. Ele recebe os seguintes 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.