Lunatik est un cadre pour scripter le noyau Linux avec Lua. Il est composé par l'interpréteur LUA modifié pour s'exécuter dans le noyau; un pilote de périphérique (écrit en Lua =)) et un outil de ligne de commande pour charger et exécuter les scripts et gérer les environnements d'exécution à partir de l'espace utilisateur; Une API C pour charger et exécuter les scripts et gérer les environnements d'exécution à partir du noyau; et les API LUA pour les installations de noyau contraignantes aux scripts LUA.
Voici un exemple de pilote de périphérique de caractère écrit en Lua à l'aide de Lunatik pour générer des caractères imprimables ASCII aléatoires:
-- /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 )Installez les dépendances (ici pour Debian / Ubuntu, pour être adaptée à sa distribution):
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 Compiler et installer 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 Une fois terminé, le script debian_kernel_postinst_lunatik.sh à partir d'outils / peut être copié dans /etc/kernel/postinst.d/ : Cela garantit que lunatik (et également les LIB nécessaires xdp ) seront compilés sur la mise à niveau du noyau.
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 : chargement des modules de noyau lunatikunload : déchargez les modules du noyau lunatikreload : Recharger les modules du noyau lunatikstatus : montrer quels modules de noyau lunatik sont actuellement chargéslist : afficher quels environnements d'exécution sont actuellement en cours d'exécutionrun : Créez un nouvel environnement d'exécution pour exécuter le script /lib/modules/lua/<script>.luaspawn : Créez un nouvel environnement d'exécution et engendrez un fil pour exécuter le script /lib/modules/lua/<script>.luastop : Arrêtez l'environnement d'exécution créé pour exécuter le script <script>default : Démarrez un REP (LIRE - EVAL - LOOPE IMPRINT) Lunatik 3.4 est basé sur LUA 5.4 adapté pour fonctionner dans le noyau.
Lunatik ne prend pas en charge l'arithmétique à virgule flottante, il ne prend donc pas en charge les métaméthodes __div ni __pow et le nombre de types n'a que l' entier du sous-type.
Lunatik ne prend pas en charge les bibliothèques IO et OS, et les identificateurs donnés des bibliothèques suivantes:
Lunatik modifie les identificateurs suivants:
"Lua 5.4-kernel" ."/lib/modules/lua/?.lua;/lib/modules/lua/?/init.lua" .Lunatik ne prend pas en charge Lual_stream, Lual_execResult, Lual_fileresult, Luaopen_io et Luaopen_OS.
Lunatik modifie Lual_Openlibs pour supprimer Luaopen_IO et LUAOPEN_OS.
#include <lunatik.h> int lunatik_runtime ( lunatik_object_t * * pruntime , const char * script , bool sleep ); lunatik_runtime () crée un nouvel environnement runtime puis charge et exécute le script /lib/modules/lua/<script>.lua comme point d'entrée pour cet environnement. Il ne doit être appelé que à partir du contexte du processus . L'environnement runtime est un objet Lunatik qui détient un état Lua. Les objets Lunatik sont Special Lua UserData qui détiennent également un type de verrouillage et un compteur de référence. Si sleep est vrai , lunatik_runtime () utilisera un mutex pour verrouiller l'environnement runtime et l'indicateur GFP_KERNEL pour allouer une nouvelle mémoire plus tard sur les appels lunatik_run (). Sinon, il utilisera un Spinlock et GFP_atomic. lunatik_runtime () ouvre les bibliothèques standard LUA présentes sur Lunatik. En cas de succès, lunatik_runtime () définit l'adresse pointée par pruntime et l'espace supplémentaire de Lua avec un pointeur pour le nouvel environnement runtime créé, définit le compteur de référence à 1 , puis renvoie 0 . Sinon, il renvoie -ENOMEM , si une mémoire insuffisante est disponible; ou -EINVAL , s'il ne parvient pas à charger ou à exécuter le 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 () ferme l'état LUA créé pour cet environnement runtime et diminue le compteur de référence. Une fois que le compteur de référence est décrémenté à zéro, le type de verrouillage et la mémoire allouée à l'environnement runtime sont libérés. Si l'environnement runtime a été publié, il renvoie 1 ; Sinon, il renvoie 0 .
void lunatik_run ( lunatik_object_t * runtime , < inttype > ( * handler )(...), < inttype > & ret , ...); Lunatik_run () verrouille l'environnement runtime et appelle le handler passant dans l'état LUA associé comme le premier argument suivi des arguments variadiques. Si l'état LUA a été fermé, ret est défini avec -ENXIO ; Sinon, ret est défini avec le résultat de l'appel handler(L, ...) . Ensuite, il restaure la pile LUA et déverrouille l'environnement runtime . Il est défini comme une 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 () incréments le compteur de référence de cet object (par exemple, environnement runtime ).
int lunatik_putobject ( lunatik_object_t * object ); lunatik_putObject () diminue le compteur de référence de cet object (par exemple, environnement runtime ). Si l' object a été libéré, il renvoie 1 ; Sinon, il renvoie 0 .
lunatik_object_t * lunatik_toruntime ( lua_State * L ); lunatik_toruntime () renvoie l'environnement runtime référencé par l'espace supplémentaire de L
La bibliothèque lunatik fournit une prise en charge pour charger et exécuter les scripts et gérer les environnements d'exécution à partir de LUA.
lunatik.runtime(script [, sleep]) lunatik.runtime () crée un nouvel environnement d'exécution, puis charge et exécute le script /lib/modules/lua/<script>.lua comme point d'entrée pour cet environnement. Il renvoie un objet Lunatik représentant l'environnement runtime . Si sleep est vrai ou omis, il utilisera un mutex et gfp_kernel; Sinon, il utilisera un Spinlock et GFP_atomic. lunatik.runtime () ouvre les bibliothèques standard LUA présentes sur Lunatik.
runtime:stop() Exécution: stop () arrête l'environnement runtime et efface sa référence à partir de l'objet d'exécution.
runtime:resume([obj1, ...]) Runtime: repume () reprend l'exécution d'un runtime . Les valeurs obj1, ... sont passées comme les arguments à la fonction renvoyée sur la création runtime . Si le runtime a cédé, resume() le redémarre; Les valeurs obj1, ... sont passées comme les résultats du rendement.
La bibliothèque device prend en charge l'écriture de pilotes d'appareils de caractère dans LUA.
device.new(driver) Device.new () renvoie un nouvel objet device et installe son driver dans le système. Le driver doit être défini comme une table contenant le champ suivant:
name : String Définition du nom du périphérique; Il est utilisé pour créer le fichier de périphérique (par exemple, /dev/<name> ). La table driver peut contenir éventuellement les champs suivants:
read : Fonction de rappel pour gérer l'opération de lecture sur le fichier de périphérique. Il reçoit la table driver comme le premier argument suivi de deux entiers, la length à lire et le offset du fichier. Il doit renvoyer une chaîne et, éventuellement, le updated offset . Si la longueur de la chaîne retournée est supérieure à la length demandée, la chaîne sera corrigée sur cette length . Si le updated offset n'est pas renvoyé, le offset sera mis à jour avec offset + length .write : fonction de rappel pour gérer l'opération d'écriture sur le fichier de périphérique. Il reçoit la table driver comme le premier argument suivi de la chaîne à écrire et d'un entier comme offset du fichier. Il peut retourner éventuellement la length écrite suivie du updated offset . Si la longueur retournée est supérieure à la length demandée, la longueur retournée sera corrigée. Si le updated offset n'est pas renvoyé, le offset sera mis à jour avec offset + length .open : fonction de rappel pour gérer l'opération ouverte sur le fichier de périphérique. Il reçoit la table driver et il est censé ne rien retourner.release : Fonction de rappel pour gérer l'opération de libération sur le fichier de périphérique. Il reçoit la table driver et il est censé ne rien retourner.mode : un entier spécifiant le mode de fichier de périphérique. Si un rappel d'opération n'est pas défini, l' device renvoie -ENXIO en VFS sur son accès.
device.stop(dev) , dev:stop() Device.stop () supprime un driver périphérique spécifié par l'objet dev du système.
La bibliothèque linux prend en charge certaines installations du noyau Linux.
linux.random([m [, n]])Linux.Random () imite le comportement de Math.Random, mais Binding <Linux / Random.h> GET_RANDOM_U32 () et GET_RANDOM_U64 () API.
Lorsqu'il est appelé sans arguments, produit un entier avec tous les bits (pseudo) aléatoire. Lorsqu'il est appelé avec deux entiers m et n , Linux.Random () renvoie un entier pseudo-aléatoire avec une distribution uniforme dans la plage [m, n] . L'appel math.random(n) , pour un n positif, équivaut à math.random(1, n) .
linux.statLinux.Stat est un tableau qui exporte <linux / stat.h> des drapeaux entiers vers Lua.
"IRWXUGO" : autorisation de lire , d'écrire et d'exécuter pour l'utilisateur , le groupe et autres ."IRUGO" : autorisation uniquement de lire pour l'utilisateur , le groupe et autres ."IWUGO" : autorisation uniquement d' écrire pour l'utilisateur , le groupe et autres ."IXUGO" : autorisation uniquement d' exécuter pour l'utilisateur , le groupe et autres . linux.schedule([timeout [, state]]) Linux.Schedule () définit l' state de tâche actuel et fait que les millisecondes timeout sont écoulées. Si timeout est omis, il utilise MAX_SCHEDULE_TIMEOUT . Si state est omis, il utilise task.INTERRUPTIBLE .
linux.taskLinux.Task est un tableau qui exporte des indicateurs d'état de tâche vers Lua.
"RUNNING" : la tâche s'exécute sur un processeur ou attend d'être exécutée."INTERRUPTIBLE" : La tâche attend un signal ou une ressource (sommeil)."UNINTERRUPTIBLE" : se comporte comme "interruptible" à l'exception que le signal ne réveillera pas la tâche."KILLABLE" : se comporte comme "sans interruption" à l'exception que les signaux mortels réveilleront la tâche."IDLE" : se comporte comme "sans interruption" à l'exception qu'il évite la comptabilité Loadavg. linux.time()Linux.Time () renvoie l'heure actuelle dans les nanosecondes depuis l'époque.
linux.errnoLinux.errno est un tableau qui exporte <UAPI / ASM-Generic / Errno-Base.h> Flags vers Lua.
"PERM" : l'opération non autorisée."NOENT" : aucun fichier ou répertoire de ce type."SRCH" : pas de tel processus."INTR" : appel du système interrompu."IO" : Erreur d'E / S."NXIO" : pas de tel périphérique ou adresse."2BIG" :, Liste des arguments trop longtemps."NOEXEC" : Erreur de format EXEC."BADF" : mauvais numéro de fichier."CHILD" : pas de processus d'enfant."AGAIN" : réessayez."NOMEM" : hors mémoire."ACCES" : autorisation refusée."FAULT" : mauvaise adresse."NOTBLK" : périphérique de bloc requis."BUSY" : appareil ou ressource occupé."EXIST" : le fichier existe."XDEV" : lien croisé."NODEV" : pas de ce type."NOTDIR" : pas un répertoire."ISDIR" : est un répertoire."INVAL" : argument non valide."NFILE" : débordement de table de fichiers."MFILE" : trop de fichiers ouverts."NOTTY" : pas une machine à écrire."TXTBSY" : fichier texte occupé."FBIG" : fichier trop grand."NOSPC" : il ne reste pas d'espace sur l'appareil."SPIPE" : recherche illégale."ROFS" : système de fichiers en lecture seule."MLINK" : trop de liens."PIPE" : tuyau cassé."DOM" : argument mathématique hors du domaine de la func."RANGE" : Résultat mathématique non représentable. linux.hton16(num)Linux.hton16 () convertit l'ordre des octets de l'hôte en ordre du réseau pour un entier 16 bits.
linux.hton32(num)Linux.hton32 () convertit l'ordre des octets de l'hôte en ordre d'octet de réseau pour un entier 32 bits.
linux.hton64(num)Linux.hton64 () convertit l'ordre des octets de l'hôte en ordre du réseau pour un entier 64 bits.
linux.ntoh16(num)Linux.ntoh16 () convertit l'ordre des octets du réseau pour héberger l'ordre des octets pour un entier 16 bits.
linux.ntoh32(num)Linux.ntoh32 () convertit l'ordre des octets du réseau pour héberger l'ordre des octets pour un entier 32 bits.
linux.ntoh64(num)Linux.ntoh64 () convertit l'ordre des octets du réseau pour héberger l'ordre des octets pour un entier 64 bits.
linux.htobe16(num)Linux.htobe16 () convertit l'ordre des octets hôte en ordre d'octet Big-endian pour un entier 16 bits.
linux.htobe32(num)Linux.htobe32 () convertit l'ordre des octets hôte en commande d'octets en gros enlan pour un entier 32 bits.
linux.htobe64(num)Linux.htobe64 () convertit l'ordre des octets hôte en commande d'octets en gros enlan pour un entier 64 bits.
linux.be16toh(num)Linux.be16toh () convertit l'ordre des octets Big-Endan pour héberger l'ordre des octets pour un entier 16 bits.
linux.be32toh(num)Linux.be32toh () convertit l'ordre des octets Big-Endan pour héberger l'ordre des octets pour un entier 32 bits.
linux.be64toh(num)Linux.be64toh () convertit l'ordre des octets Big-Endan pour accueillir l'ordre des octets pour un entier 64 bits.
linux.htole16(num)linux.htole16 () convertit l'ordre des octets hôte en ordre d'octet petit-endian pour un entier 16 bits.
linux.htole32(num)Linux.htole32 () convertit l'ordre des octets de l'hôte en ordre d'octet petit-endian pour un entier 32 bits.
linux.htole64(num)linux.htole64 () convertit l'ordre des octets hôte en ordre d'octet petit-endian pour un entier 64 bits.
linux.le16toh(num)Linux.le16toh () convertit l'ordre des octets Little-Endan pour héberger l'ordre des octets pour un entier 16 bits.
linux.le32toh(num)Linux.le32toh () convertit l'ordre des octets Little-Endan pour héberger l'ordre des octets pour un entier 32 bits.
linux.le64toh(num)Linux.le64toh () convertit l'ordre des octets Little-Endan pour héberger l'ordre des octets pour un entier 64 bits.
La bibliothèque notifier prend en charge les chaînes de notificateurs de noyau.
notifier.keyboard(callback) Notifier.KeyBoard () Renvoie un nouvel objet notifier de clavier et l'installe dans le système. La fonction callback est appelée chaque fois qu'un événement de clavier de console se produit (par exemple, une touche a été enfoncée ou publiée). Ce callback reçoit les arguments suivants:
event : Les événements disponibles sont définis par la table NOTIFIR.KBD.down : true , si la touche est enfoncée; false , s'il est libéré.shift : true , si la touche Maj est maintenue; false , sinon.key : Keycode ou Keysym en fonction de event . La fonction callback peut renvoyer les valeurs définies par la table notificateur.Notify.
notifier.kbdNotifier.kbd est un tableau qui exporte des drapeaux KBD vers Lua.
"KEYCODE" : Keyboard Keycode , appelé avant les autres."UNBOUND_KEYCODE" : Keycoche du clavier qui n'est lié à aucun autre."UNICODE" : clavier Unicode."KEYSYM" : Keyboard Keysym ."POST_KEYSYM" : appelé après l'interprétation du clavier Keysym . notifier.netdevice(callback) notifier.netdevice () renvoie un nouvel objet NetDevice notifier et l'installe dans le système. La fonction callback est appelée chaque fois qu'un événement NetDevice de console se produit (par exemple, une interface réseau a été connectée ou déconnectée). Ce callback reçoit les arguments suivants:
event : Les événements disponibles sont définis par la table notificaire.netdev.name : le nom de l'appareil. La fonction callback peut renvoyer les valeurs définies par la table notificateur.Notify.
notifier.netdevnotifier.netdev est un tableau qui exporte des indicateurs NetDev vers Lua.
notifier.notifynotifier.notify est un tableau que les exportations notifient les drapeaux vers Lua.
"DONE" : Je m'en fiche."OK" : me convient."BAD" : Mauvaise action / veto."STOP" : Nettoyer la façon de revenir du notificateur et d'arrêter d'autres appels. notfr:delete() NotFr: Delete () supprime un notifier spécifié par l'objet notfr du système.
La bibliothèque socket prend en charge la manipulation de réseautage du noyau. Cette bibliothèque a été inspirée par le projet GSOC de Chengzhi Tan.
socket.new(family, type, protocol) Socket.new () crée un nouvel objet socket . Cette fonction reçoit les arguments suivants:
family : Les familles d'adresses disponibles sont définies par la table Socket.AF.sock : les types disponibles sont présents sur la table Socket.Sock.protocol : Les protocoles disponibles sont définis par la table Socket.Ipproto. socket.afSocket.af est un tableau qui exporte des familles (AF) vers Lua.
"UNSPEC" : non spécifié."UNIX" : Sockets de domaine Unix."LOCAL" : Nom POSIX pour af_unix."INET" : Protocole IP Internet."AX25" : radio amateur AX.25."IPX" : Novell Ipx."APPLETALK" : Appletalk DDP."NETROM" : Radio Amateur Net / Rom."BRIDGE" : pont multiprotocol."ATMPVC" : ATM PVCS."X25" : Réservé pour le projet X.25."INET6" : IP Version 6."ROSE" : Radio amateur X.25 PLP."DEC" : Réservé pour le projet Decnet."NETBEUI" : Réservé pour le projet 802.2llc."SECURITY" : Pseudo AF de rappel de sécurité."KEY" : API de gestion des clés PF_KEY."NETLINK" : NetLink."ROUTE" : Alias pour imiter 4,4bsd."PACKET" : Famille de paquets."ASH" : Ash."ECONET" : Acorn Econet."ATMSVC" : ATM SVCS."RDS" : sockets RDS."SNA" : Linux SNA Project (Nutters!)."IRDA" : Sockets Irda."PPPOX" : PPPOX Sockets."WANPIPE" : sockets API Wanpipe."LLC" : Linux LLC."IB" : adresse infiniband native."MPLS" : MPLS."CAN" : réseau de zone de contrôleur."TIPC" : TIPC Sockets."BLUETOOTH" : sockets Bluetooth."IUCV" : Sockets UICV."RXRPC" : sockets rxrpc."ISDN" : Sockets mal."PHONET" : Phonet Sockets."IEEE802154" : Sockets IEEE802154."CAIF" : Caif Sockets."ALG" : sockets d'algorithme."NFC" : Sockets NFC."VSOCK" : VSOCKETS."KCM" : Multiplexor de connexion du noyau."QIPCRTR" : Routeur Qualcomm IPC."SMC" : numéro de réserve pour la famille du protocole PF_SMC qui réutilise la famille d'adresses af_inet."XDP" : Sockets XDP."MCTP" : Protocole de transport des composants de gestion."MAX" : Maximum. socket.sockSocket.Sock est un tableau qui exporte des types de socket (chaussettes):
"STREAM" : silm de flux (connexion)."DGRAM" : Datagram (Conn.less) Socket."RAW" : prise brute."RDM" : message de livraison de manière fiable."SEQPACKET" : prise de paquets séquentiels."DCCP" : prise de protocole de contrôle de congestion de Datagram."PACKET" : moyen spécifique de Linux d'obtenir des paquets au niveau du développement.et drapeaux (chaussette):
"CLOEXEC" : n / a."NONBLOCK" : N / A. socket.ipprotoSocket.Ipproto est un tableau qui exporte des protocoles IP (IPPROTO) vers Lua.
"IP" : protocole factice pour TCP."ICMP" : Protocole de message de contrôle Internet."IGMP" : Protocole de gestion des groupes Internet."IPIP" : tunnels IPIP (les anciens tunnels KA9Q utilisent 94)."TCP" : protocole de contrôle de transmission."EGP" : protocole de passerelle extérieure."PUP" : protocole de chiot."UDP" : protocole de datagramme utilisateur."IDP" : Protocole XNS IDP."TP" : SO PROPRIPTION PROTOCOL CLASSE 4."DCCP" : protocole de contrôle de congestion de Datagram."IPV6" : tunneling IPv6-in-ipv4."RSVP" : protocole RSVP."GRE" : tunnels GRE Cisco (RFC 1701,1702)."ESP" : Protocole de charge utile de sécurité de l'encapsulation."AH" : Protocole d'en-tête d'authentification."MTP" : Protocole de transport de multidiffusion."BEETPH" : en-tête pseudo d'option IP pour la betterave."ENCAP" : En-tête d'encapsulation."PIM" : multidiffusion indépendante du protocole."COMP" : Protocole d'en-tête de compression."SCTP" : protocole de transport de contrôle des cours d'eau."UDPLITE" : UDP-Lite (RFC 3828)."MPLS" : MPLS dans IP (RFC 4023)."ETHERNET" : Enterrent-within-ipv6 Encapsulation."RAW" : paquets IP bruts."MPTCP" : connexion TCP multipath. sock:close() Sock: Close () supprime l'objet sock du système.
sock:send(message, [addr [, port]]) Sock: Send () envoie un message de chaîne via la sock . Si la famille d'adresses sock est af.INET , elle attend les arguments suivants:
addr : integer décrivant l'adresse IPv4 de destination.port : integer décrivant le port IPv4 de destination.Sinon:
addr : chaîne emballée décrivant l'adresse de destination. sock:receive(length, [flags [, from]]) Sock: Recee () reçoit une chaîne avec des octets allant jusqu'à length à travers la sock socket. Les indicateurs de message disponible sont définis par la table Socket.msg. Si from est true , il renvoie le message reçu suivi de l'adresse du pair. Sinon, il ne renvoie que le message reçu.
socket.msgSocket.msg est un tableau qui exporte des indicateurs de message vers Lua.
"OOB" : n / a."PEEK" : n / a."DONTROUTE" : n / a."TRYHARD" : synonyme de "DONTROUTE" pour DECNET."CTRUNC" : n / a."PROBE" : n'envoyez pas. Seule le chemin de sonde Fe pour MTU."TRUNC" : n / a."DONTWAIT" : IO non bloquant."EOR" : fin du disque."WAITALL" : attendez une demande complète."FIN" : n / a."SYN" : n / a."CONFIRM" : confirmer la validité du chemin."RST" : n / a."ERRQUEUE" : Fetch Message à partir de la file d'attente d'erreur."NOSIGNAL" : Ne générez pas de sigpipe."MORE" : l'expéditeur en enverra plus."WAITFORONE" : recvmmsg (): Bloquer jusqu'à plus de 1 paquets disponibles."SENDPAGE_NOPOLICY" : sendPage () interne: ne demandez pas de politique."SENDPAGE_NOTLAST" : sendPage () interne: pas la dernière page."BATCH" : sendmmsg (): Plus de messages à venir."EOF" : n / a."NO_SHARED_FRAGS" : sendPage () interne: les frags de page ne sont pas partagés."SENDPAGE_DECRYPTED" : sendPage () Internal: la page peut transporter du texte brut et nécessiter un chiffrement."ZEROCOPY" : utilisez les données utilisateur dans le chemin du noyau."FASTOPEN" : Envoyez des données dans TCP Syn."CMSG_CLOEXEC" : Définissez Close_on_Exec pour le descripteur de fichiers reçu via SCM_RIGHTS. sock:bind(addr [, port]) Sock: bind () lie la socket sock à une adresse donnée. Si la famille d'adresses sock est af.INET , elle attend les arguments suivants:
addr : integer décrivant l'adresse IPv4 hôte.port : integer décrivant le port IPv4 hôte.Sinon:
addr : chaîne emballée décrivant l'adresse hôte. sock:listen([backlog]) Sock: écouter () déplace la socket sock vers l'état d'écoute.
backlog : Taille de file d'attente des connexions en attente. S'il est omis, il utilise SomaxConn par défaut. sock:accept([flags]) Sock: Accept () accepte une connexion sur Socket sock . Il renvoie un nouvel objet socket . Les drapeaux disponibles sont présents sur la table Socket.Sock.
sock:connect(addr [, port] [, flags]) Sock: Connect () connecte la socket sock à l'adresse addr . Si la famille d'adresses sock est af.INET , elle attend les arguments suivants:
addr : integer décrivant l'adresse IPv4 de destination.port : integer décrivant le port IPv4 de destination.Sinon:
addr : chaîne emballée décrivant l'adresse de destination.Les drapeaux disponibles sont présents sur la table Socket.Sock.
Pour les prises de données, addr est l'adresse à laquelle les datagrammes sont envoyés par défaut et la seule adresse à partir de laquelle les datagrammes sont reçus. Pour les prises de flux, tente de se connecter à addr .
sock:getsockname() Sock: getockName () Obtenez l'adresse que la sock de socket est liée. Si la famille d'adresses sock est af.INET , elle renvoie ce qui suit:
addr : integer décrivant l'adresse IPv4 délimitée.port : integer décrivant le port IPv4 délimité.Sinon:
addr : chaîne emballée décrivant l'adresse bornée. sock:getpeername() Sock: getpeername () Obtenez l'adresse à laquelle la sock de socket est connectée. Si la famille d'adresses sock est af.INET , elle renvoie ce qui suit:
addr : integer décrivant l'adresse IPv4 du pair.port : integer décrivant le port IPv4 du pair.Sinon:
addr : chaîne emballée décrivant l'adresse du pair. La bibliothèque socket.inet fournit une prise en charge des sockets IPv4 de haut niveau.
inet.tcp() Inet.tcp () crée une nouvelle socket en utilisant la famille d'adresses af.inet, le type sock.stream et le protocole ipproto.tcp. Il remplace les méthodes socket pour utiliser les adresses comme notation de nombres et de points (par exemple, "127.0.0.1" ), au lieu des entiers.
inet.udp() INET.UDP () Crée une nouvelle socket en utilisant AF.inet Address Family, Sock.Dgram Type et Ipproto.udp Protocol. Il remplace les méthodes socket pour utiliser les adresses comme notation de nombres et de points (par exemple, "127.0.0.1" ), au lieu des entiers.
udp:receivefrom(length [, flags]) UDP: ReceedFrom () n'est qu'un alias à sock:receive(length, flags, true) .
La bibliothèque rcu prend en charge le mécanisme de synchronisation de la mise à jour en lecture-copie du noyau (RCU). Cette bibliothèque a été inspirée par le projet GSOC de Caio Messias.
rcu.table([size]) RCU.Table () crée un nouvel objet rcu.table qui lie la table de hachage générique du noyau. Cette fonction reçoit comme argument le nombre de seaux arrondi jusqu'à la puissance suivante de 2. La taille par défaut est 1024 . La clé doit être une chaîne et la valeur doit être un objet Lunatik ou NIL.
La bibliothèque thread prend en charge les primitives du thread de noyau.
thread.run(runtime, name) thread.run () crée un nouvel objet thread et le réveille. Cette fonction reçoit les arguments suivants:
runtime : l'environnement d'exécution pour exécuter une tâche dans le thread de noyau créé. La tâche doit être spécifiée en renvoyant une fonction sur le script chargé dans l'environnement runtime .name : chaîne représentant le nom du thread (par exemple, comme indiqué sur ps ). thread.shouldstop() Thread.ShouldStop () Renvoie true si Thread.stop () a été appelé; Sinon, il renvoie false .
thread.current() thread.current () renvoie un objet thread représentant la tâche actuelle.
thrd:stop() thrd: stop () définit thread.souldstop () sur le fil thrd pour retourner true, réveille thrd et attend qu'il quitte.
thrd:task() thrd: task () renvoie un tableau contenant les informations de tâche de ce thread (par exemple, "CPU", "commande", "pid" et "tgid").
La bibliothèque fib prend en charge la base d'informations de transfert du noyau.
fib.newrule(table, priority)fib.newrule () lie l'API du noyau FIB_NL_NEWRULE; Il crée une nouvelle règle FIB qui correspond à la table de routage spécifiée avec la privilège spécifiée. Cette fonction est similaire à la règle IP de commande utilisateur-espace Ajouté fourni par iPROUTE2.
fib.delrule(table, priority)fib.delrule () lie l'API du noyau FIB_NL_DELRULE; Il supprime une règle FIB qui correspond à la table de routage spécifiée avec la privilège spécifiée. Cette fonction est similaire à la règle IP de commande utilisateur de la commande fournie par iPROUTE2.
La bibliothèque data prend en charge la liaison de la mémoire système à Lua.
data.new(size) data.new () crée un nouvel objet data qui alloue les octets size .
d:getnumber(offset) D: getNumber () extrait un LUA_INTEGER de la mémoire référencée par un objet data et un offset d'octet, à partir de zéro.
d:setnumber(offset, number) D: setNumber () Insérez un number LUA_INTEGER dans la mémoire référencée par un objet data et un offset d'octet, à partir de zéro.
d:getbyte(offset) D: GetByte () extrait un octet de la mémoire référencée par un objet data et un offset d'octet, à partir de zéro.
d:setbyte(offset, byte) D: setByte () Insérez un octet dans la mémoire référencée par un objet data et un offset d'octet, à partir de zéro.
d:getstring(offset[, length]) D: getString () extrait une chaîne avec des octets length de la mémoire référencée par un objet data et un offset d'octet, à partir de zéro. Si length est omise, elle extrait tous les octets du offset à la fin des data .
d:setstring(offset, s) D: setString () Insérez la chaîne s dans la mémoire référencée par un objet data et un offset d'octet, à partir de zéro.
d:getint8(offset) D: GetInt8 (D, Offset) extrait un entier 8 bits signé à partir de la mémoire référencée par un objet data et un offset d'octet, à partir de zéro.
d:setint8(offset, number) D: SetInt8 () inserte un numéro 8 bits signé dans la mémoire référencée par un objet data et un offset d'octet, à partir de zéro.
d:getuint8(offset) D: getUint8 () extrait un entier 8 bits non signé de la mémoire référencée par un objet data et un offset d'octet, à partir de zéro.
d:setuint8(offset, number) D: setuint8 () inserte un numéro 8 bits non signé dans la mémoire référencée par un objet data et un offset d'octet, à partir de zéro.
d:getint16(offset) D: GetInt16 () extrait un entier de 16 bits signé à partir de la mémoire référencée par un objet data et un offset d'octet, à partir de zéro.
d:setint16(offset, number) D: SetInt16 () Insère un numéro de 16 bits signé dans la mémoire référencée par un objet data et un offset d'octet, à partir de zéro.
d:getuint16(offset) D: getUint16 () extrait un entier 16 bits non signé de la mémoire référencée par un objet data et un offset d'octet, à partir de zéro.
d:setuint16(offset, number) D: SetUint16 () Insère un numéro 16 bits non signé dans la mémoire référencée par un objet data et un offset d'octet, à partir de zéro.
d:getint32(offset) D: getInt32 () extrait un entier 32 bits signé de la mémoire référencée par un objet data et un offset d'octet, à partir de zéro.
d:setint32(offset, number) D: SetInt32 () Insère un numéro 32 bits signé dans la mémoire référencée par un objet data et un offset d'octet, à partir de zéro.
d:getuint32(offset) D: getUint32 () extrait un entier 32 bits non signé de la mémoire référencée par un objet data et un offset d'octet, à partir de zéro.
d:setuint32(offset, number) D: SetUint32 () inserte un numéro 32 bits non signé dans la mémoire référencée par un objet data et un offset d'octet, à partir de zéro.
d:getint64(offset) D: getInt64 () extrait un entier 64 bits signé de la mémoire référencée par un objet data et un offset d'octet, à partir de zéro.
d:setint64(offset, number) D: SetInt64 () inserte un numéro 64 bits signé dans la mémoire référencée par un objet data et un offset d'octet, à partir de zéro.
La bibliothèque probe prend en charge les sondes du noyau.
probe.new(symbol|address, handlers) sonde.new () renvoie un nouvel objet probe pour surveiller un symbol de noyau (chaîne) ou address (UserData léger) et installe ses handlers dans le système. Le handler doit être défini comme un tableau contenant le champ suivant:
pre : fonction à appeler avant l'instruction sondée. Il reçoit le symbol ou address , suivi d'une fermeture qui peut être appelée pour montrer les registres du CPU et la pile dans le journal système.post : fonction à appeler après l'instruction sondée. Il reçoit le symbol ou address , suivi d'une fermeture qui peut être appelée pour montrer les registres du CPU et la pile dans le journal système. p:stop() P: stop () supprime les gestionnaires probe du système.
p:enable(bool) P: Activer () Active ou désactive les gestionnaires probe , en conséquence à bool .
La bibliothèque syscall prend en charge les adresses et numéros d'appel système.
syscall.address(number) syscall.address () renvoie l'adresse d'appel système (UserData léger) référencé par le number donné.
syscall.number(name) syscall.number () renvoie le numéro d'appel système référencé par le name donné.
La bibliothèque syscall.table fournit une prise en charge de la traduction des noms d'appels système aux adresses (UserData léger).
La bibliothèque xdp prend en charge le sous-système de chemin de données de données de Kernel Express (XDP). Cette bibliothèque a été inspirée par le projet GSOC de Victor Nogueira.
xdp.attach(callback) XDP.Attach () enregistre une fonction callback à l' runtime actuelle à appeler à partir d'un programme XDP / EBPF chaque fois qu'il appelle BPF_LUAXDP_RUN KFUNC. Ce callback reçoit les arguments suivants:
buffer : un objet data représentant le tampon réseau.argument : un objet data contenant l'argument passé par le programme XDP / EBPF. La fonction callback peut renvoyer les valeurs définies par la table xdp.action.
xdp.detach() XDP.DETACH () Démante le callback associé à l' runtime actuelle, le cas échéant.
xdp.actionXDP.Action est un tableau qui exporte des indicateurs XDP_Action à Lua.
"ABORTED" : indique que le programme XDP a été abandonné, généralement en raison d'une erreur."DROP" : Spécifie que le paquet doit être abandonné, le jetant entièrement."PASS" : permet au paquet de passer à la pile de réseau Linux."TX" : transmet le paquet sur la même interface qu'il a été reçu."REDIRECT" : redirige le paquet vers une autre interface ou un contexte de traitement. La bibliothèque xtable prend en charge le développement d'extensions NetFilter XTABLE.
xtable.match(opts)xtable.match () Renvoie un nouvel objet xtable pour les extensions de correspondance. Cette fonction reçoit les arguments suivants:
opts : un tableau contenant les champs suivants:name : chaîne représentant le nom d'extension xtable.revision : entier représentant la révision de l'extension xtable.family : Adressez la famille, l'une de NetFilter.family.proto : Numéro de protocole, l'un de Socket.ipproto.hooks : crochet pour attacher l'extension, une valeur de l'une ou l'autre de la table des crochets - netfilter.inet_hooks, netfilter.bridge_hooks et netfilter.arp_hooks (note: netfilter.netdev_hooks n'est pas disponible pour l'héritage x_tables). (Par exemple - 1 << inet_hooks.LOCAL_OUT ).match : fonction à appeler pour faire correspondre les paquets. Il reçoit les arguments suivants:skb (ReadOnly): un objet data représentant le tampon de socket.par : Une table contenant des champs hotdrop , thoff (en-tête de transport) et fragoff (Fragment Offset).userargs : une chaîne LUA est passée du module UsersPace XTable.true si le paquet correspond à l'extension; Sinon, il doit retourner false .checkentry : fonction à appeler pour vérifier l'entrée. Cette fonction reçoit userargs comme argument.destroy : la fonction à appeler pour détruire l'extension xtable. Cette fonction reçoit userargs comme argument. xtable.target(opts)xtable.target () Renvoie un nouvel objet xtable pour l'extension cible. Cette fonction reçoit les arguments suivants:
opts : un tableau contenant les champs suivants:name : chaîne représentant le nom d'extension xtable.revision : entier représentant la révision de l'extension xtable.family : Adressez la famille, l'une de NetFilter.family.proto : Numéro de protocole, l'un de Socket.ipproto.hooks : crochet pour attacher l'extension, une valeur de l'une ou l'autre de la table des crochets - netfilter.inet_hooks, netfilter.bridge_hooks et netfilter.arp_hooks (note: netfilter.netdev_hooks n'est pas disponible pour l'héritage x_tables). (Par exemple - 1 << inet_hooks.LOCAL_OUT ).target : fonction à appeler pour cibler les paquets. Il reçoit les arguments suivants:skb : un objet data représentant le tampon de socket.par (ReadOnly): une table contenant des champs hotdrop , thoff (en-tête de transport) et fragoff (Fragment Offset).userargs : une chaîne LUA est passée du module UsersPace XTable.checkentry : fonction à appeler pour vérifier l'entrée. Cette fonction reçoit userargs comme argument.destroy : la fonction à appeler pour détruire l'extension xtable. Cette fonction reçoit userargs comme argument. La bibliothèque netfilter prend en charge le nouveau système de crochet NetFilter.
netfilter.register(ops) NetFilter.Register () enregistre un nouveau crochet NetFilter avec la table ops donnée. Cette fonction reçoit les arguments suivants:
ops : un tableau contenant les champs suivants:pf : Famille de protocole, l'un de NetFilter.familyhooknum : crochet pour attacher le filtre, une valeur de l'une ou l'autre du tableau des crochets - netfilter.inet_hooks, netfilter.bridge_hooks, netfilter.arp_hooks et netfilter.netdev_hooks. (Par exemple - inet_hooks.LOCAL_OUT + 11 ).priority : priorité du crochet. L'une des valeurs des tables NetFilter.IP_Priority ou NetFilter.bridge_priority.hook : fonction à appeler pour le crochet. Il reçoit les arguments suivants: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.