Lunatik是用Lua腳本腳本腳本的框架。它是由修改以在內核中運行的LUA解釋器組成的;設備驅動程序(用LUA =)和一個命令行工具加載和運行腳本並從用戶空間管理運行時環境; C API加載和運行腳本並從內核管理運行時環境;和LUA API將內核設施綁定到LUA腳本。
這是使用lua使用lunatik編寫的字符設備驅動程序的示例,以生成隨機的ASCII可打印字符:
-- /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 )安裝依賴項(在這里為Debian/Ubuntu,要適應一個人的分佈):
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編譯並安裝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完成後,可以將來自Tools/的debian_kernel_postinst_lunatik.sh腳本複製到/etc/kernel/postinst.d/ :這確保lunatik (以及所需的xdp Libs)將在內核上升級。
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 :加載Lunatik內核模塊unload :卸載Lunatik內核模塊reload :重新加載Lunatik內核模塊status :顯示當前加載了哪些Lunatik內核模塊list :顯示當前正在運行的運行時環境run :創建一個新的運行時環境來運行腳本/lib/modules/lua/<script>.lua lua/<script> .luaspawn :創建一個新的運行時環境,並產生線程以運行腳本/lib/modules/lua/<script>.lua lua/<script> .luastop :停止創建的運行時環境來運行腳本<script>default :啟動一個repl(read – eval – Print循環) Lunatik 3.4基於LUA 5.4適用於內核運行。
lunatik不支持浮點算術,因此它不支持__div和__pow metAmethods,並且類型編號僅具有子類型整數。
Lunatik不支持IO和OS庫,以及以下庫中給定的標識符:
Lunatik修改以下標識符:
"Lua 5.4-kernel" 。"/lib/modules/lua/?.lua;/lib/modules/lua/?/init.lua" 。lunatik不支持lual_stream,lual_execresult,lual_fileresult,luaopen_io和luaopen_os。
lunatik修改了lual_openlibs以刪除luaopen_io和luaopen_os。
#include <lunatik.h> int lunatik_runtime ( lunatik_object_t * * pruntime , const char * script , bool sleep ); lunatik_runtime()創建一個新的runtime環境,然後加載並運行腳本/lib/modules/lua/<script>.lua lua/pripcript> .lua作為此環境的入口點。必須僅從過程上下文中調用它。 runtime環境是持有LUA狀態的Lunatik對象。 Lunatik對像是特殊的LUA USERDATA,它還擁有鎖類型和參考計數器。如果sleep是正確的,則Lunatik_runtime()將使用MUTEX鎖定runtime環境和GFP_KERNEL標誌,以稍後在lunatik_run()調用上分配新內存。 Otherwise, it will use a spinlock and GFP_ATOMIC. lunatik_runtime()打開Lua標準庫。如果成功, lunatik_runtime()設置了pruntime和Lua的額外空間指向的地址,則使用用於新創建的runtime環境的指針,將參考計數器設置為1 ,然後返回0 。 Otherwise, it returns -ENOMEM , if insufficient memory is available;或-EINVAL ,如果未能加載或運行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() closes the Lua state created for this runtime environment and decrements the reference counter.一旦將參考計數器降低至零,則會發佈為runtime環境分配的鎖定類型和內存。如果發布了runtime環境,則返回1 ;否則,它將返回0 。
void lunatik_run ( lunatik_object_t * runtime , < inttype > ( * handler )(...), < inttype > & ret , ...); lunatik_run()鎖定runtime環境,並將handler通過關聯的lua狀態為第一個參數,然後是variadic參數。如果LUA狀態已關閉,則將ret設置為-ENXIO ;否則, ret設置為handler(L, ...)調用的結果。然後,它還原Lua堆棧並解鎖runtime環境。它被定義為宏。
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()增加了此object的參考計數器(例如, runtime環境)。
int lunatik_putobject ( lunatik_object_t * object ); lunatik_putobject()降低了此object的參考計數器(例如, runtime環境)。如果object已釋放,則返回1 ;否則,它將返回0 。
lunatik_object_t * lunatik_toruntime ( lua_State * L ); lunatik_toruntime()返回L的額外空間引用的runtime環境。
lunatik庫為加載和運行腳本並管理LUA的運行時環境提供了支持。
lunatik.runtime(script [, sleep]) lunatik.runtime()創建一個新的運行時環境,然後加載並運行腳本/lib/modules/lua/<script>.lua lua/pripcript> .lua作為此環境的入口點。它返回代表runtime環境的lunatik對象。 If sleep is true or omitted, it will use a mutex and GFP_KERNEL;否則,它將使用Spinlock和GFP_Atomic。 Lunatik.runtime()打開Lua標準庫在Lunatik上。
runtime:stop()運行時:stop()停止runtime環境,並從運行時對象清除其引用。
runtime:resume([obj1, ...])運行時:簡歷()恢復執行runtime 。值obj1, ...作為參數傳遞給runtime創建返回的函數。 If the runtime has yielded, resume() restarts it;值obj1, ...值作為產量的結果傳遞。
device庫為LUA中的字符設備驅動程序提供支持。
device.new(driver) device.new()返回一個新的device對象並將其driver安裝在系統中。 driver必須定義為包含以下字段的表:
name :定義設備名稱的字符串;它用於創建設備文件(例如/dev/<name> )。 driver表可能會選擇包含以下字段:
read : callback function to handle the read operation on the device file.它接收driver表作為第一個參數,然後是兩個整數,要讀取的length和文件offset 。它應該返回字符串,並選擇updated offset 。如果返回的字符串的長度大於請求的length ,則將校正該字符串為該length 。如果未返回updated offset ,則將使用offset + length更新offset 。write : callback function to handle the write operation on the device file.它接收driver表作為第一個參數,然後是要編寫的字符串和整數作為文件offset 。它可能會選擇返回書面length ,然後再返回updated offset 。如果返回的長度大於請求的length ,則將糾正返回的長度。如果未返回updated offset ,則將使用offset + length更新offset 。open : callback function to handle the open operation on the device file.它收到driver表,預計什麼也不會返回。release : callback function to handle the release operation on the device file.它收到driver表,預計什麼也不會返回。mode :指定設備文件模式的整數。如果未定義操作回調,則該device在訪問時將-ENXIO返回到VFS。
device.stop(dev) , dev:stop() device.stop()刪除系統從系統中dev的設備driver 。
linux庫為某些Linux內核設施提供了支持。
linux.random([m [, n]])linux.random()模仿Math.random的行為,但綁定<linux/andural.h>的get_random_u32()和get_random_u64()apis。
當無參數調用時,會隨機產生一個具有所有位(偽)的整數。當使用兩個整數m和n調用時, Linux.random()將返回一個偽隨機整數,範圍內具有均勻分佈[m, n] 。對於正n ,呼叫math.random(n)等同於math.random(1, n) 。
linux.statLinux.Stat是一張表,將<linux/stat.h>整數標誌導出到LUA。
"IRWXUGO" :允許為用戶,組和其他人讀,寫和執行。"IRUGO" :僅允許閱讀用戶,組和其他人。"IWUGO" : permission only to write for user , group and other ."IXUGO" :僅對用戶,組和其他人執行的權限。 linux.schedule([timeout [, state]]) linux.schedule()設置當前任務state ,並使IT睡眠直至timeout毫秒。 If timeout is omitted, it uses MAX_SCHEDULE_TIMEOUT .如果省略state ,它使用task.INTERRUPTIBLE 。
linux.taskLinux.Task是一張將任務狀態標誌導出到LUA的表。
"RUNNING" :任務是在CPU上執行或等待執行。"INTERRUPTIBLE" :任務正在等待信號或資源(睡眠)。"UNINTERRUPTIBLE" :表現為“可中斷”,但信號不會喚醒任務。"KILLABLE" :表現為“不間斷”,但致命信號將喚醒任務。"IDLE" :表現為“不間斷”,但它避免了LoadAvg會計。 linux.time()Linux.Time()自時代以來返回納秒秒的當前時間。
linux.errnolinux.errno是一張表,該表導出<uapi/asm-generic/errno-base.h> flags flags。
"PERM" :不允許操作。"NOENT" :沒有此類文件或目錄。"SRCH" :沒有這樣的過程。"INTR" :中斷的系統調用。"IO" :I/O錯誤。"NXIO" :沒有這樣的設備或地址。"2BIG" :,參數列表太長。"NOEXEC" :exec格式錯誤。"BADF" :壞文件編號。"CHILD" :沒有孩子的過程。"AGAIN" :重試。"NOMEM" :不記憶。"ACCES" :拒絕許可。"FAULT" :不良地址。"NOTBLK" :需要塊設備。"BUSY" :設備或資源忙。"EXIST" :文件存在。"XDEV" :跨設備鏈接。"NODEV" :沒有這樣的設備。"NOTDIR" :不是目錄。"ISDIR" :是一個目錄。"INVAL" :無效的參數。"NFILE" :文件表溢出。"MFILE" :太多的打開文件。"NOTTY" :不是打字機。"TXTBSY" :文本文件忙。"FBIG" :文件太大。"NOSPC" :設備上沒有剩餘的空間。"SPIPE" :非法尋求。"ROFS" :僅讀取文件系統。"MLINK" :鏈接太多。"PIPE" :折斷的管道。"DOM" :來自Func領域的數學論點。"RANGE" :數學結果不可代表。 linux.hton16(num)Linux.hton16()將主體字節訂單轉換為16位整數的網絡字節訂單。
linux.hton32(num)Linux.hton32()將主體字節訂單轉換為32位整數的網絡字節訂單。
linux.hton64(num)Linux.hton64()將主體字節訂單轉換為64位整數的網絡字節訂單。
linux.ntoh16(num)linux.ntoh16()將網絡字節訂單轉換為託管字節訂單16位整數。
linux.ntoh32(num)Linux.NTOH32()將網絡字節訂單轉換為託管32位整數的字節訂單。
linux.ntoh64(num)Linux.NTOH64()將網絡字節訂單轉換為託管字節訂單64位整數。
linux.htobe16(num)linux.htobe16()將主體字節訂單轉換為16位整數的大型字節訂單。
linux.htobe32(num)linux.htobe32()將主體字節順序轉換為32位整數的大型字節訂單。
linux.htobe64(num)Linux.htobe64()將主體字節訂單轉換為64位整數的大型字節訂單。
linux.be16toh(num)linux.be16toh()將大端字節訂單轉換為16位整數的主字節訂單。
linux.be32toh(num)linux.be32toh()將大型字節訂單轉換為32位整數的主字節訂單。
linux.be64toh(num)linux.be64toh()將大型字節訂單轉換為64位整數託管字節訂單。
linux.htole16(num)linux.htole16()將主體字節訂單轉換為16位整數的Little-Endian字節訂單。
linux.htole32(num)linux.htole32()將主體字節訂單轉換為32位整數的Little-Endian字節訂單。
linux.htole64(num)linux.htole64()將主體字節訂單轉換為64位整數的Little-Endian字節訂單。
linux.le16toh(num)linux.le16toh()將小型字節訂單轉換為16位整數的主字節訂單。
linux.le32toh(num)linux.le32toh()將小型字節訂單轉換為32位整數託管字節訂單。
linux.le64toh(num)linux.le64toh()將小型字節訂單轉換為64位整數託管字節訂單。
notifier庫為內核通知鏈提供了支持。
notifier.keyboard(callback) notifier.keyboard()返回一個新的鍵盤notifier對象並將其安裝在系統中。每當發生控制台鍵盤事件時(例如,已按下或釋放鍵)時,調用callback函數。此callback收到以下參數:
event :可用事件由notifier.kbd表定義。down : true ,如果按下鍵; false ,如果發布。shift : true ,如果持有換檔密鑰; false ,否則。key :根據event的不同,密鑰代碼或密鑰。 callback函數可能會返回notifier.notify表所定義的值。
notifier.kbdnotifier.kbd是一張將KBD標誌導出到LUA的表。
"KEYCODE" :鍵盤鍵代碼,在其他任何內容之前。"UNBOUND_KEYCODE" :鍵盤鍵盤未綁定到任何其他鍵盤。"UNICODE" :鍵盤Unicode。"KEYSYM" :鍵盤鍵。"POST_KEYSYM" :在鍵盤鍵鍵解釋後調用。 notifier.netdevice(callback) notifier.netdevice()返回新的NetDevice notifier對象並將其安裝在系統中。每當發生遊戲機NetDevice事件發生時,都會調用callback函數(例如,已連接或斷開網絡接口)。此callback收到以下參數:
event :可用事件由notifier.netdev表定義。name :設備名稱。 callback函數可能會返回notifier.notify表所定義的值。
notifier.netdevnotifier.netdev是一張將NetDev標誌出口到LUA的表。
notifier.notifynotifier.notify是一張將通知標誌導出到lua的表。
"DONE" :不在乎。"OK" :適合我。"BAD" :壞/否決動作。"STOP" :從通知者返回並停止進一步呼叫的清潔方法。 notfr:delete() notfr:delete()從系統中刪除了notfr對象指定的notifier 。
socket庫為內核網絡處理提供了支持。該圖書館的靈感來自Chengzhi Tan的GSOC項目。
socket.new(family, type, protocol) socket.new()創建一個新的socket對象。此功能會收到以下參數:
family :可用的地址系列由套接字定義。sock :可用類型存在於插座表上。protocol :可用協議由socket.ipproto表定義。 socket.afsocket.af.af是一張桌子,該表將家族(AF)出口到LUA。
"UNSPEC" :未指定。"UNIX" :unix域插座。"LOCAL" :af_unix的POSIX名稱。"INET" :Internet IP協議。"AX25" :業餘無線電AX.25。"IPX" :Novell IPX。"APPLETALK" :appletalk ddp。"NETROM" :業餘無線電網/ROM。"BRIDGE" :多協議橋。"ATMPVC" :ATM PVC。"X25" :保留用於X.25項目。"INET6" :IP版本6。"ROSE" :業餘無線電X.25 PLP。"DEC" :保留用於DECNET項目。"NETBEUI" :保留給802.2LLC項目。"SECURITY" :安全回調偽AF。"KEY" :PF_KEY密鑰管理API。"NETLINK" :Netlink。"ROUTE" :模仿4.4BSD的別名。"PACKET" :數據包家庭。"ASH" :灰。"ECONET" :Acorn Econet。"ATMSVC" :ATM SVCS。"RDS" :RDS插座。"SNA" :Linux SNA項目(Nutters!)。"IRDA" :IRDA插座。"PPPOX" :PPPOX插座。"WANPIPE" :wanpipe api插座。"LLC" :Linux LLC。"IB" :本地Infiniband地址。"MPLS" :MPLS。"CAN" :控制器區域網絡。"TIPC" :TIPC插座。"BLUETOOTH" :藍牙插座。"IUCV" :IUCV插座。"RXRPC" :RXRPC插座。"ISDN" :MISDN插座。"PHONET" :Phonet插座。"IEEE802154" :IEEE802154插座。"CAIF" :CAIF插座。"ALG" :算法插座。"NFC" :NFC插座。"VSOCK" :vsockets。"KCM" :內核連接多路復用器。"QIPCRTR" :高通IPC路由器。"SMC" :PF_SMC協議家族的儲備編號,該家族重用AF_INET地址家庭。"XDP" :XDP插座。"MCTP" :管理組件傳輸協議。"MAX" :最大值。 socket.sockSocket.Sock是一張表格類型(襪子)的表:
"STREAM" :流(連接)插座。"DGRAM" : datagram (conn.less) socket."RAW" :原始插座。"RDM" :可靠交付的消息。"SEQPACKET" :順序數據包套接字。"DCCP" :數據報擁塞控制協議套接字。"PACKET" :Linux在DEV級別獲取數據包的特定方式。和標誌(襪子):
"CLOEXEC" : n/a."NONBLOCK" :n/a。 socket.ipprotosocket.ipproto是一張將IP協議(IPPROTO)導出到LUA的表。
"IP" :TCP的虛擬協議。"ICMP" :Internet控制消息協議。"IGMP" :Internet組管理協議。"IPIP" :IPIP隧道(較舊的KA9Q隧道使用94)。"TCP" :傳輸控制協議。"EGP" :外部網關協議。"PUP" :PUP協議。"UDP" :用戶數據報協議。"IDP" : XNS IDP protocol."TP" :因此運輸協議第4類。"DCCP" :數據報擁塞控制協議。"IPV6" :IPv6-IN-IPV4隧道。"RSVP" :RSVP協議。"GRE" : Cisco GRE tunnels (rfc 1701,1702)."ESP" :封裝安全有效負載協議。"AH" :身份驗證標頭協議。"MTP" :多播運輸協議。"BEETPH" :甜菜的IP選項偽標頭。"ENCAP" :封裝標頭。"PIM" :協議獨立的多播。"COMP" :壓縮標頭協議。"SCTP" :流控制傳輸協議。"UDPLITE" :udp-lite(RFC 3828)。"MPLS" :IP中的MPLS(RFC 4023)。"ETHERNET" :以太網 - 與IPV6封裝。"RAW" : Raw IP packets."MPTCP" :Multipath TCP連接。 sock:close()襪子:CLOSE()從系統中刪除sock對象。
sock:send(message, [addr [, port]])襪子:send()通過套筒sock發送字符串message 。如果sock地址家族是af.INET ,那麼它會期望以下論點:
addr :描述目標IPv4地址的integer 。port :描述目標IPv4端口的integer 。否則:
addr :描述目標地址的包裝字符串。 sock:receive(length, [flags [, from]])襪子:接收()通過插座sock接收一個具有length字節的字符串。 The available message flags are defined by the socket.msg table. If from is true , it returns the received message followed by the peer's address. Otherwise, it returns only the received message.
socket.msgsocket.msg是一張將消息標誌導出到lua的表。
"OOB" :n/a。"PEEK" :n/a。"DONTROUTE" : n/a."TRYHARD" :Decnet的"DONTROUTE"的同義詞。"CTRUNC" : n/a."PROBE" :不要發送。僅探測MTU的Fe。"TRUNC" :n/a。"DONTWAIT" :非塊IO。"EOR" :記錄結束。"WAITALL" :等待完整的請求。"FIN" :n/a。"SYN" : n/a."CONFIRM" :確認路徑有效性。"RST" : n/a."ERRQUEUE" :從錯誤隊列中獲取消息。"NOSIGNAL" :請勿生成sigpipe。"MORE" :發件人將發送更多。"WAITFORONE" :recvmmsg():阻止1個以上的數據包。"SENDPAGE_NOPOLICY" :sendpage()內部:不應用策略。"SENDPAGE_NOTLAST" :sendpage()內部:不是最後一頁。"BATCH" :sendmmsg():更多消息即將到來。"EOF" :n/a。"NO_SHARED_FRAGS" : sendpage() internal: page frags are not shared."SENDPAGE_DECRYPTED" :sendpage()內部:頁面可能攜帶純文本並需要加密。"ZEROCOPY" :在內核路徑中使用用戶數據。"FASTOPEN" :在TCP SYN中發送數據。"CMSG_CLOEXEC" : Set close_on_exec for file descriptor received through SCM_RIGHTS. sock:bind(addr [, port])襪子:綁定()將套筒sock綁定到給定的地址。如果sock地址家族是af.INET ,那麼它會期望以下論點:
addr :描述主機IPv4地址的integer 。port :描述主機IPv4端口的integer 。否則:
addr :描述主機地址的包裝字符串。 sock:listen([backlog])襪子:listing()將插座sock移到聽力狀態。
backlog :等待連接隊列大小。如果省略,它將SomaxConn用作默認值。 sock:accept([flags])襪子:Accept()接受插座sock上的連接。 It returns a new socket object.可用標誌存在於插座表上。
sock:connect(addr [, port] [, flags])襪子:Connect()將套筒sock連接到地址addr 。如果sock地址家族是af.INET ,那麼它會期望以下論點:
addr :描述目標IPv4地址的integer 。port :描述目標IPv4端口的integer 。否則:
addr :描述目標地址的包裝字符串。可用標誌存在於插座表上。
對於數據報插座, addr是默認情況下發送數據報的地址,也是收到數據報的唯一地址。對於流插座,嘗試連接到addr 。
sock:getsockname()襪子:getsockname()獲取插座sock綁定的地址。如果sock地址家族是af.INET ,則返回以下內容:
addr :描述有限的IPv4地址的integer 。port : integer describing the bounded IPv4 port.否則:
addr :描述有界地址的包裝字符串。 sock:getpeername()襪子:getPeername()獲取插座sock連接的地址。如果sock地址家族是af.INET ,則返回以下內容:
addr :描述對等的IPv4地址的integer 。port :描述對等的IPv4端口的integer 。否則:
addr :描述對等地址的包裝字符串。socket.inet庫提供了對高級IPv4插座的支持。
inet.tcp() inet.tcp() creates a new socket using af.INET address family, sock.STREAM type and ipproto.TCP protocol.它覆蓋了將地址用作數字和點符號(例如"127.0.0.1" )的socket方法,而不是整數。
inet.udp() inet.udp() creates a new socket using af.INET address family, sock.DGRAM type and ipproto.UDP protocol.它覆蓋了將地址用作數字和點符號(例如"127.0.0.1" )的socket方法,而不是整數。
udp:receivefrom(length [, flags]) udp:receerfrom()只是sock:receive(length, flags, true) 。
The rcu library provides support for the kernel Read-copy update (RCU) synchronization mechanism. This library was inspired by Caio Messias' GSoC project.
rcu.table([size]) rcu.table()創建一個新的rcu.table對象,該對象綁定了內核通用哈希表。此函數作為參數接收到的框架數量達到2的下一個功率。默認大小為1024 。鑰匙必須是字符串,值必須是lunatik對像或零。
The thread library provides support for the kernel thread primitives.
thread.run(runtime, name) thread.run() creates a new thread object and wakes it up.此功能會收到以下參數:
runtime :用於在創建內核線程中運行任務的運行時環境。必須通過在runtime環境中加載的腳本上返回函數來指定任務。name :表示線程名稱的字符串(例如,如ps所示)。 thread.shouldstop()如果thread.stop()調用thread.shouldstop(),則返回true ;否則,它返回false 。
thread.current() thread.current()返回代表當前任務的thread對象。
thrd:stop() thrd:stop()在螺紋上設置thread.shouldstop()thr thr thrd true,wakes thrd ,然後等待它退出。
thrd:task() thrd:task()返回包含此thread任務信息的表(例如,“ CPU”,“命令”,“ PID”和“ TGID”)。
fib庫為內核轉發信息庫提供了支持。
fib.newrule(table, priority)fib.newrule()綁定內核fib_nl_newrule api;它創建了一個新的FIB規則,該規則將指定的路由表與指定的PrivimiTy匹配。此功能類似於用戶空間命令IP規則添加IPROUTE2提供的添加。
fib.delrule(table, priority)fib.delrule()綁定內核fib_nl_delrule api;它刪除了將指定路由表與指定的優先屬性匹配的FIB規則。此功能類似於IPROUTE2提供的用戶空間命令IP規則DEL。
data提供了將系統內存與LUA結合的支持。
data.new(size) data.new()創建一個新的data對象,該數據對象分配size字節。
d:getnumber(offset) D:getNumber()從data對象offset的內存中提取lua_integer,從零開始。
d:setnumber(offset, number) D:setNumber()將lua_integer number插入data對象引用的內存和字節offset ,從零開始。
d:getbyte(offset) D:getByte()從data對象引用的內存中提取字節和一個字節offset ,從零開始。
d:setbyte(offset, byte) D:setByte()將一個字節插入data對象引用的內存和一個字節offset ,從零開始。
d:getstring(offset[, length]) D:getString()從data對象offset的內存中提取帶有length字節的字符串,從零開始。如果省略了length ,它將從data offset提取所有字節。
d:setstring(offset, s) D:setString()將字符串s插入data對象引用的內存和一個字節offset ,從零開始。
d:getint8(offset) D:getInt8(d,偏移)從data對象引用的內存和字節offset中提取一個符號的8位整數,從零開始。
d:setint8(offset, number) D:setInt8()將簽名的8位編號插入data對象引用的內存和一個字節offset ,從零開始。
d:getuint8(offset) D:getUint8()從data對象引用的內存和字節offset中提取一個無符號的8位整數,從零開始。
d:setuint8(offset, number) D:setUint8()將一個未符號的8位編號插入data對象引用的內存和一個字節offset ,從零開始。
d:getint16(offset) D:GetInt16()從data對象引用的內存和字節offset從零開始提取簽名的16位整數。
d:setint16(offset, number) D:setInt16()將簽名的16位數字插入data對象引用的內存和一個字節offset ,從零開始。
d:getuint16(offset) D:getUint16()從data對象引用的內存和字節offset中提取一個未簽名的16位整數,從零開始。
d:setuint16(offset, number) D:setUint16()將一個未簽名的16位編號插入data對象引用的內存和一個字節offset ,從零開始。
d:getint32(offset) D:GetInt32()從data對象引用的內存和字節offset從零開始,從零開始提取一個符號的32位整數。
d:setint32(offset, number) D:setInt32()將簽名的32位編號插入data對象引用的內存和一個字節offset ,從零開始。
d:getuint32(offset) D:getUint32()從data對象引用的內存和字節offset中提取一個無符號的32位整數,從零開始。
d:setuint32(offset, number) D:setUint32()將一個未簽名的32位編號插入data對象引用的內存和一個字節offset ,從零開始。
d:getint64(offset) D:GetInt64()從data對象引用的內存和字節offset從零開始提取符號的64位整數。
d:setint64(offset, number) D:setInt64()將簽名的64位編號插入data對象引用的內存和一個字節offset ,從零開始。
probe庫提供了對內核探針的支持。
probe.new(symbol|address, handlers) Probe.new()返回一個新的probe對象,用於監視內核symbol (字符串)或address (Light userData),並在系統中安裝其handlers 。 handler必須定義為包含以下字段的表:
pre :在探測指令之前要調用功能。它接收symbol或address ,然後收到封閉式,可以打電話給CPU寄存器和堆疊系統日誌中。post :探測指令後要調用的功能。它接收symbol或address ,然後收到封閉式,可以打電話給CPU寄存器和堆疊系統日誌中。 p:stop() P:stop()刪除系統中的probe處理程序。
p:enable(bool) P:Enable()啟用或禁用probe處理程序,相應地bool 。
syscall庫為系統呼叫地址和數字提供了支持。
syscall.address(number) syscall.address()返回給定number引用的系統呼叫地址(LIGHT USERDATA)。
syscall.number(name) syscall.number()返回給定name引用的系統呼叫號碼。
syscall.table庫提供了將系統呼叫名稱轉換為地址(Light userData)的支持。
xdp庫為內核Express數據路徑(XDP)子系統提供了支持。該圖書館的靈感來自Victor Nogueira的GSOC項目。
xdp.attach(callback) XDP.ATTACH()每當調用BPF_LUAXDP_RUN KFUNC時,將callback函數登錄到當前runtime ,要從XDP/EBPF程序調用。此callback收到以下參數:
buffer :代表網絡緩衝區的data對象。argument :包含XDP/EBPF程序傳遞的參數的data對象。 callback函數可能會返回XDP.Action表所定義的值。
xdp.detach() xdp.detach()取消註冊與當前runtime相關聯的callback (如果有)。
xdp.actionXDP.Action是將XDP_Action標誌導出到LUA的表。
"ABORTED" :表明XDP程序流產,通常是由於錯誤。"DROP" :指定應該刪除數據包,將其完全丟棄。"PASS" :允許數據包通過Linux網絡堆棧。"TX" :將數據包在接收到的相同接口上傳輸。"REDIRECT" :將數據包重定向到另一個接口或處理上下文。 xtable庫為開發NetFilter Xtable Extensions提供了支持。
xtable.match(opts)Xtable.match()返回一個新的Xtable對象,以進行匹配擴展。此功能會收到以下參數:
opts :包含以下字段的表:name :表示Xtable擴展名稱的字符串。revision :代表Xtable擴展修訂的整數。family :地址家庭,netfilter。proto :協議編號,socket.ipproto之一。hooks :將擴展名附加到一個掛鉤錶的一個值 - netfilter.inet_hooks,netfilter.bridge_hooks和netfilter.arp_hooks(注意:NetFilter.netdev_hooks不可用於遺產x_tables)。 (例如1 << inet_hooks.LOCAL_OUT )。match :匹配數據包的功能。它收到以下論點:skb (ReadOnly):代表套接字緩衝區的data對象。par :包含hotdrop , thoff (傳輸標頭偏移)和fragoff (片段偏移)字段的表。userargs :從用戶空間Xtable模塊傳遞的LUA字符串。true ;否則,它必須返回false 。checkentry :呼叫要檢查條目的功能。此功能接收userargs作為參數。destroy :被要求銷毀Xtable擴展的功能。此功能接收userargs作為參數。 xtable.target(opts)Xtable.target()返回一個新的Xtable對像以進行目標擴展。此功能會收到以下參數:
opts :包含以下字段的表:name :表示Xtable擴展名稱的字符串。revision :代表Xtable擴展修訂的整數。family :地址家庭,netfilter。proto :協議編號,socket.ipproto之一。hooks :將擴展名附加到一個掛鉤錶的一個值 - netfilter.inet_hooks,netfilter.bridge_hooks和netfilter.arp_hooks(注意:NetFilter.netdev_hooks不可用於遺產x_tables)。 (例如1 << inet_hooks.LOCAL_OUT )。target :要調用定位數據包的功能。它收到以下論點:skb :代表套接字緩衝區的data對象。par (ROADONLY):包含hotdrop , thoff (傳輸標頭偏移)和fragoff (片段偏移)字段的表。userargs :從用戶空間Xtable模塊傳遞的LUA字符串。checkentry :呼叫要檢查條目的功能。此功能接收userargs作為參數。destroy :被要求銷毀Xtable擴展的功能。此功能接收userargs作為參數。 netfilter庫為新的NetFilter鉤系統提供了支持。
netfilter.register(ops) NetFilter.Register()在給定的ops表中註冊了一個新的NetFilter鉤子。此功能會收到以下參數:
ops :包含以下字段的表:pf :協議家庭,NetFilter。hooknum :將過濾器附加到一個掛鉤錶 - netfilter.inet_hooks,netfilter.bridge_hooks,netfilter.arp_hooks和netfilter.netdev_hooks。 (Eg - inet_hooks.LOCAL_OUT + 11 ).priority :掛鉤的優先級。 netfilter.ip_priority或netfilter.bridge_priority表中的值之一。hook :要調用鉤子的函數。它收到以下論點:skb :代表套接字緩衝區的data對象。netfilter.familyNetFilter.FAMILY是一張桌子,將家庭出口到LUA。
"UNSPEC" :未指定。"INET" :Internet協議版本4。"IPV4" : Internet Protocol version 4."IPV6" :Internet協議版本6。"ARP" :地址分辨率協議。"NETDEV" :設備入口和出口路徑"BRIDGE" :以太網橋。 netfilter.actionNetFilter.Action是將NetFilter動作導出到LUA的表。
"DROP" : NF_DROP 。數據包被丟棄。它沒有被任何其他網絡層轉發,處理或看到。"ACCEPT" : NF_ACCEPT 。 The packet is accepted and passed to the next step in the network processing chain."STOLEN" : NF_STOLEN 。該數據包由處理程序接收,然後處理。"QUEUE" : NF_QUEUE 。該數據包已排隊用於用戶空間處理。"REPEAT" : NF_REPEAT 。 The packet is sent through the hook chain again."STOP" : NF_STOP . Processing of the packet stops."CONTINUE" : XT_CONTINUE .返回數據包應繼續在同一表中遍歷規則。"RETURN" : XT_RETURN 。 Return the packet to the previous chain. netfilter.inet_hooksnetfilter.inet_hooks是一張將INET NetFilter鉤子導出到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 。該數據包將轉發給另一個主機。"LOCAL_OUT" : NF_INET_LOCAL_OUT 。數據包由本地系統生成。"POST_ROUTING" : NF_INET_POST_ROUTING .數據包即將發送。 netfilter.bridge_hooksnetfilter.bridge_hooks是一張桌子,將NetFilter鉤子導出到LUA。
"PRE_ROUTING" : NF_BR_PRE_ROUTING .首先調用掛鉤,在諮詢前向數據庫之前運行。"LOCAL_IN" : NF_BR_LOCAL_IN 。調用原本為橋樑配置的機器的數據包調用。"FORWARD" : NF_BR_FORWARD 。被要求橋接到同一邏輯橋設備的另一個端口的框架。"LOCAL_OUT" : NF_BR_LOCAL_OUT .要求將通過橋傳輸的本地發起的數據包。"POST_ROUTING" : NF_BR_POST_ROUTING .要求所有本地生成的數據包和所有橋接數據包netfilter.arp_hooksnetfilter.arp_hooks是一張將ARP NetFilter鉤子導出到LUA的表。
"IN" : NF_ARP_IN 。該數據包由網絡堆棧接收。"OUT" : NF_ARP_OUT 。數據包由本地系統生成。"FORWARD" : NF_ARP_FORWARD .該數據包將轉發給另一個主機。 netfilter.netdev_hooksnetfilter.netdev_hooks是一張將NetDev NetFilter鉤子導出到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是一張將NetFilter IPv4/IPv6優先級級別導出到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是一張將NetFilter橋樑優先級級別導出到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_LASTluaxt用戶空間庫為生成Xtable擴展名的用戶空間代碼提供了支持。
To build the library, the following steps are required:
usr/lib/xtable and create a libxt_<ext_name>.lua file.luaxt )來註冊Xtable擴展程序的回調。LUAXTABLE_MODULE=<ext_name> make以構建擴展名, LUAXTABLE_MODULE=<ext_name> make install (作為root)將用戶空間插件安裝到系統中。現在,使用iptables正常加載擴展名。
luaxt.match(opts)luaxt.match() returns a new luaxt object for match extensions.此功能會收到以下參數:
opts :包含以下字段的表:revision :代表Xtable擴展修訂版的整數(必須與相應的內核擴展中使用相同)。family : address family, one of luaxt.familyhelp : function to be called for displaying help message for the extension.init :調用以初始化擴展名的函數。此功能接收一個可用於設置userargs par表。 ( par.userargs = "mydata" )print :打印參數的函數。此功能接收由init或parse功能設置userargs 。save :呼籲保存參數的功能。此功能接收由init或parse功能設置userargs 。parse :呼籲解析命令行參數的函數。此功能接收一個可以用於設置userargs和flags par表。 ( par.userargs = "mydata" )final_check :要調用函數以最終檢查參數。 This function receives flags set by the parse function. luaxt.target(opts)luaxt.target()返回一個用於目標擴展的新的luaxt對象。此功能會收到以下參數:
opts :包含以下字段的表:revision :代表Xtable擴展修訂版的整數(必須與相應的內核擴展中使用相同)。family : address family, one of luaxt.familyhelp :呼叫功能以顯示擴展程序的幫助消息。init :調用以初始化擴展名的函數。 This function receives an par table that can be used to set userargs . ( par.userargs = "mydata" )print :打印參數的函數。此功能接收由init或parse功能設置userargs 。save :呼籲保存參數的功能。此功能接收由init或parse功能設置userargs 。parse :呼籲解析命令行參數的函數。此功能接收一個可以用於設置userargs和flags par表。 ( par.userargs = "mydata" )final_check :要調用函數以最終檢查參數。 This function receives flags set by the parse function. luaxt.familyluaxt.Family是一張出口向Lua的家庭的桌子。
"UNSPEC" :未指定。"INET" : Internet Protocol version 4."IPV4" : Internet Protocol version 4."IPV6" : Internet Protocol version 6."ARP" :地址分辨率協議。"NETDEV" : Device ingress and egress path"BRIDGE" :以太網橋。completion The completion library provides support for the kernel completion primitives.
任務完成是一種用於協調多個線程執行的同步機制,類似於pthread_barrier ,它允許線程等待在進行之前發生特定事件,以確保以無賽車的方式完成某些任務。
completion.new() completion.new() creates a new completion object.
c:complete()c:postement()信號在等待此完成的單個線程。
c:wait([timeout]) c:wait() waits for completion of a task until the specified timeout expires.超時以毫秒為單位指定。如果省略了timeout參數,則無限期等待。通過小於零的超時值會導致不確定的行為。等待事件的線程可能會被信號中斷,例如,當thread.stop被調用時。因此,此功能可以以三種方式返回:
truenil, "timeout"nil, "interrupt"spyglass is a kernel script that implements a keylogger inspired by the spy kernel module.此內核腳本記錄設備( /dev/spyglass )中按鍵的密鑰。 If the keysym is a printable character, spyglass logs the keysym itself;否則,它會記錄ASCII代碼的助記符(例如, <del>代表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是一個內核腳本,它實現了用於鎖定和解鎖控制台鍵盤的Konami代碼。 When the user types ↑ ↑ ↓ ↓ ← → ← → LCTRL LALT , the keyboard will be locked ;也就是說,系統將停止處理任何鍵,直到用戶再次鍵入相同的鍵序列為止。
sudo make examples_install # installs examples
sudo lunatik run examples/keylocker # runs keylocker
<↑> <↑> <↓> <↓> <←> <→> <←> <→> <LCTRL> <LALT> # locks keyboard
<↑> <↑> <↓> <↓> <←> <→> <←> <→> <LCTRL> <LALT> # unlocks keyboard
TAP是一個內核腳本,可使用AF_PACKET套接字實現嗅探器。 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
共享是一個內核腳本,可使用RCU,數據,套接字和線程實現內存鍵值存儲。
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是一款以內核腳本實現的迴聲服務器。
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.它打印出每個系統調用的次數,因為駕駛員已安裝。
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是由XDP/EBPF程序組成的內核擴展程序,用於過濾HTTPS會話和LUA內核腳本,以過濾SNI TLS擴展。 This kernel extension drops any HTTPS request destinated to a blacklisted server.
編譯並安裝libbpf , libxdp和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 program例如,感謝Docker,測試很容易。假設Docker已安裝並運行:
sudo xdp-loader load -m skb docker0 https.o
sudo journalctl -ft kerneldocker run --rm -it alpine/curl https://ebpf.io系統日誌(在第一個終端中)應顯示filter_sni: ebpf.io DROP , docker run…應返回curl: (35) OpenSSL SSL_connect: SSL_ERROR_SYSCALL in connection to ebpf.io:443 。
此其他SNI過濾器使用NetFilter API。
dnsblock is a kernel script that uses the lunatik xtable library to filter DNS packets.該腳本將任何出站DNS數據包丟棄,與用戶提供的黑名單相匹配的問題。
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是一個內核腳本,它使用Lunatik Xtable庫將DNS響應從公共IP更改為私有IP(如果目標IP匹配用戶提供的響應)。例如,如果用戶想將DNS響應從192.168.10.1更改為域lunatik.com 192.168.10.1至10.1.2.3 ,如果將查詢發送到10.1.1.2 (一個私有客戶端),則可以使用此腳本。
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.