Lunatik

socketModule

Low-level Lua interface for kernel networking sockets.

This library provides support for creating and managing various types of sockets within the Linux kernel, enabling network communication directly from Lua scripts running in kernel space. It is inspired by Chengzhi Tan’s GSoC project.

It allows operations such as creating sockets, binding, listening, connecting, sending, and receiving data. The constants for address families, socket types, protocols, message flags and option levels and names are in linux.socket (af, sock, ipproto, msg, sol, so, ip).

An integer argument is bounded to the kernel type that takes it, an int unless a method says otherwise: one past it raises “out of bounds” instead of reaching the kernel truncated.

Sockets sleep: a script creates and uses them in a process runtime, the default, or in a spawned thread. In a softirq or hardirq runtime, socket.new and accept raise 'socket': process-context class in interrupt-context runtime.

For higher-level IPv4 TCP/UDP socket operations with string-based IP addresses (e.g., “127.0.0.1”), consider using the socket.inet library.

See also

Class socket

socket:accept ([flags=0]) Accepts a connection on a listening socket.
socket:bind (addr[, port]) Binds the socket to a local address.
socket:close () Closes the socket.
socket:connect (addr[, port[, flags=0]]) Initiates a connection on a socket.
socket:getpeername () Gets the address of the peer to which the socket is connected.
socket:getsockname () Gets the local address to which the socket is bound.
socket:listen ([backlog]) Puts a connection-oriented socket into the listening state.
socket:receive (length[, flags=0]) Receives a message from the socket.
socket:receivefrom (length[, flags=0]) Receives a message from the socket, and the address of its sender.
socket:send (message[, addr[, port]]) Sends a message through the socket.
socket:setsockopt (level, optname, value) Sets a socket option.

socket

new (family, type, protocol[, pid]) Creates a new socket object.

Class socket in detail

A kernel socket, returned by socket.new().

A method that takes an address reads it the way the family the socket was created with spells it:

  • AF_INET: two integers, the IPv4 address (e.g. from net.aton()), within 32 bits, and the port, within 16.
  • AF_INET6: a packed string of struct sockaddr_in6 past the family: the port and the flow information in network byte order, the address, and the scope id.
  • AF_PACKET: two integers, the ethertype in host byte order, within 16 bits, and the interface index, from 0.
  • AF_NETLINK: two optional integers, the port id and the multicast groups, both 0 by default and within 32 bits.
  • AF_UNIX: a string, a filesystem path or, with a leading NUL, an abstract name.
  • Other families: a packed string of the address bytes past the family.

A method that returns an address returns it the same way. An AF_PACKET one carries three more values after the interface index: the packet type (a linux.packet.type value), the hardware type (a linux.arphrd value) and the hardware address, a string of the length the kernel reports: the interface’s own for getsockname, the sender’s for receivefrom.

socket:accept ([flags=0])
Accepts a connection on a listening socket. This function is used with connection-oriented sockets (e.g., SOCK_STREAM) that have been put into the listening state by sock:listen(). The call blocks until a connection arrives, with no timeout of its own, unless flags carries O_NONBLOCK or setsockopt set a receive timeout; either makes a wait with no connection to accept answer nil, and receive says how a stop ends the wait.

Parameters

  • flags integer file status flags: O_NONBLOCK makes the call answer at once when no connection is pending instead of waiting for one. linux.socket.sock.NONBLOCK carries O_NONBLOCK on every architecture but alpha and parisc. (default 0)

Returns

    socket A new socket object representing the accepted connection; nil and "EAGAIN" when the wait ended with no connection to accept.

Raises

Error if the accept operation fails.
socket:bind (addr[, port])
Binds the socket to a local address. This is typically used on the server side before calling listen() or on connectionless sockets to specify a local port/interface for receiving.

Parameters

  • addr integer or string

    local address to bind to. Interpretation depends on the family the socket was created with:

    • AF_INET (IPv4): An integer representing the IPv4 address (e.g., from net.aton()). Use 0 (or net.aton("0.0.0.0")) to bind to all available interfaces. The port argument is also required.
    • AF_PACKET: An integer representing the ethernet protocol in host byte order (e.g., 0x0003 for ETH_P_ALL, 0x88CC for ETH_P_LLDP) The port argument is also required.
    • AF_NETLINK: The port id, 0 to let the kernel pick one; port carries the multicast groups.
    • AF_UNIX: A filesystem path, or, when its first byte is NUL (e.g. "\0name"), a name in the abstract namespace, which is registered as the exact bytes given, so a peer of any kind reaches it by the same string. The empty string asks the kernel to pick a name (autobind).
    • Other families: A packed string directly representing parts of the family-specific address structure.
  • port integer local port or interface index. - AF_INET: TCP/UDP port number. - AF_PACKET: Network interface index (e.g., from linux.ifindex("eth0")). - AF_NETLINK: Multicast groups to join. (optional)

Returns

    nil

Raises

Error if the bind operation fails (e.g., address already in use, invalid address).

See also

Usage

-- Bind TCP/IPv4 socket to localhost, port 8080
tcp_server_sock:bind(net.aton("127.0.0.1"), 8080)

-- Bind AF_PACKET socket to protocol ETH_P_LLDP on a specific interface
af_packet_sock:bind(0x88CC, linux.ifindex("eth0"))
socket:close ()
Closes the socket. This shuts down the socket for both reading and writing and releases associated kernel resources. A to-be-closed variable holding the socket closes it the same way, and dropping its last reference does too.

Returns

    nil
socket:connect (addr[, port[, flags=0]])
Initiates a connection on a socket. This is typically used by client sockets to establish a connection to a server. For datagram sockets, this sets the default destination address for send and the only address from which datagrams are received. A TCP connect waits for the connection, with no timeout of its own, unless flags carries O_NONBLOCK or setsockopt set a send timeout; either makes a wait that ends with the connection still in progress answer nil, whether this connect or an earlier one started it, and send says how a stop ends the wait. An AF_UNIX connect waits the same way for room in the backlog of the listener it reaches, and a wait that ends with none answers false.

Parameters

  • addr integer or string

    destination address to connect to. Interpretation depends on the family the socket was created with:

    • AF_INET (IPv4): An integer representing the IPv4 address (e.g., from net.aton()). The port argument is also required.
    • AF_PACKET and AF_NETLINK: The first of the two integers the socket type describes.
    • Other families: The address as the socket type describes it.
  • port integer the address’s second integer, for AF_INET, AF_PACKET and AF_NETLINK; for any other family, flags takes this place. (optional)
  • flags integer file status flags: O_NONBLOCK makes a connect that cannot complete at once not wait for it. linux.socket.sock.NONBLOCK carries O_NONBLOCK on every architecture but alpha and parisc. (default 0)

Returns

    boolean true once connected; nil and "EINPROGRESS" when the wait ended with the connection it started still in progress, or "EALREADY" with one an earlier connect started; false when it ended with no room in an AF_UNIX listener’s backlog.

Raises

Error if the connect operation fails (e.g., connection refused, host unreachable).

Usage

tcp_client_sock:connect(net.aton("192.168.1.100"), 80)
socket:getpeername ()
Gets the address of the peer to which the socket is connected. This is typically used with connection-oriented sockets after a connection has been established, or with connectionless sockets after connect() has been called to set a default peer.

Returns

  1. integer or string

    addr peer’s address.

    • For AF_INET: An integer representing the IPv4 address (can be converted with net.ntoa()).
    • For AF_UNIX: The peer’s name, carrying its leading NUL when the name is an abstract one, and the empty string when the peer is unbound.
    • For AF_NETLINK: The peer’s port id.
    • For other families: A packed string of the address bytes past the family, of the length the kernel reports.
  2. integer port The peer’s port number for AF_INET, and its multicast groups for AF_NETLINK.

Raises

Error if the operation fails (e.g., socket not connected); EOPNOTSUPP on an AF_PACKET socket, which names no peer.

Usage

-- a connected AF_INET socket
local peer_ip_int, peer_port = connected_socket:getpeername()
print("Connected to " .. net.ntoa(peer_ip_int) .. ":" .. peer_port)
socket:getsockname ()
Gets the local address to which the socket is bound.

Returns

  1. integer or string

    addr local address.

    • For AF_INET: An integer representing the IPv4 address (can be converted with net.ntoa()).
    • For AF_PACKET: The ethertype the socket was created or bound with, in host byte order.
    • For AF_UNIX: The bound name, carrying its leading NUL when the name is an abstract one, and the empty string when the socket is unbound.
    • For AF_NETLINK: The port id.
    • For other families: A packed string of the address bytes past the family, of the length the kernel reports.
  2. integer port The local port number for AF_INET, the interface index for AF_PACKET, 0 when unbound, and the multicast groups for AF_NETLINK.
  3. integer pkttype For AF_PACKET, 0.
  4. integer hatype For AF_PACKET, the interface’s hardware type, 0 when unbound.
  5. string hwaddr For AF_PACKET, the interface’s hardware address, empty when unbound.

Raises

Error if the operation fails.

Usage

-- an AF_INET socket
local local_ip_int, local_port = my_socket:getsockname()
print("Bound to " .. net.ntoa(local_ip_int) .. ":" .. local_port)
socket:listen ([backlog])
Puts a connection-oriented socket into the listening state. This is required for server sockets (e.g., SOCK_STREAM) to be able to accept incoming connections.

Parameters

  • backlog integer maximum length of the queue for pending connections. If omitted, a system-dependent default (e.g., SOMAXCONN) is used. (optional)

Returns

    nil

Raises

Error if the listen operation fails (e.g., socket not bound, invalid state).

Usage

tcp_server_sock:listen(10)
socket:receive (length[, flags=0])
Receives a message from the socket. The call blocks until a message arrives, with no timeout of its own, unless flags carries linux.socket.msg.DONTWAIT or setsockopt set a receive timeout (linux.socket.so.RCVTIMEO_NEW); either makes a wait with nothing to read answer nil. A stop of the thread that waits ends its wait, which raises ERESTARTSYS, or EINTR under a receive timeout.

Parameters

  • length integer maximum number of bytes to receive, from 0 to INT_MAX.
  • flags integer Optional message flags (e.g., linux.socket.msg.PEEK). See the linux.socket.msg table for available flags. These can be OR’d together. (default 0)

Returns

    string received message (as a string of bytes); on a stream socket, the empty string is the end of file, once the peer has shut down its side; nil and "EAGAIN" when the wait ended with nothing to read.

Raises

Error if the receive operation fails.

See also

Usage

-- For a connected TCP socket:
local data = tcp_conn_sock:receive(1024)
print("Received:", data)
socket:receivefrom (length[, flags=0])
Receives a message from the socket, and the address of its sender. It waits as receive does. A connectionless socket (SOCK_DGRAM) names the sender of each message; TCP names none, and neither does an AF_UNIX peer that never bound, and nothing follows the message then.

Parameters

  • length integer maximum number of bytes to receive, as receive takes it.
  • flags integer message flags, as receive takes them. (default 0)

Returns

  1. string received message, as receive returns it; nil and "EAGAIN", in place of the message and the address, when the wait ended with nothing to read.
  2. integer or string addr the sender’s address (two values for AF_INET and AF_NETLINK, five for AF_PACKET). - For AF_INET: An integer representing the IPv4 address (can be converted with net.ntoa()). - For AF_PACKET: The frame’s ethertype, in host byte order. - For AF_UNIX: The sender’s name, carrying its leading NUL when the name is an abstract one. - For AF_NETLINK: The sender’s port id. - For other families: A packed string of the sender’s address past the family, of the length the protocol reports.
  3. integer port the sender’s port number for AF_INET, the index of the interface the frame arrived on for AF_PACKET, and its multicast groups for AF_NETLINK.
  4. integer pkttype For AF_PACKET, the frame’s packet type.
  5. integer hatype For AF_PACKET, the interface’s hardware type.
  6. string hwaddr For AF_PACKET, the sender’s hardware address.

Raises

Error if the receive operation fails.

See also

Usage

-- For a UDP socket, getting sender info:
local data, sender_ip_int, sender_port = udp_sock:receivefrom(1500)
print("Received from " .. net.ntoa(sender_ip_int) .. ":" .. sender_port .. ": " .. data)
socket:send (message[, addr[, port]])
Sends a message through the socket.

For connection-oriented sockets (SOCK_STREAM), addr and port are usually omitted as the connection is already established. For connectionless sockets (SOCK_DGRAM), addr and port (if applicable for the address family) specify the destination. A netlink socket always sends to an address, the kernel’s (port id 0) when none is given. The call waits for room in the send buffer, with no timeout of its own, unless setsockopt set a send timeout (linux.socket.so.SNDTIMEO_NEW), which makes a wait it ends with nothing queued answer false. A lunatik stop of the spawned thread that waits ends its wait, which raises ERESTARTSYS, or EINTR under a send timeout, when nothing was queued.

Parameters

  • message string message to send.
  • addr integer or string

    destination address, as the socket type describes it.

    • For AF_INET (IPv4) sockets: An integer representing the IPv4 address (e.g., from net.aton()).
    • For AF_PACKET: the ethertype, in host byte order.
    • For AF_NETLINK: the destination port id.
    (optional)
  • port integer the address’s second integer: the destination port number for AF_INET, the interface index for AF_PACKET, the multicast groups for AF_NETLINK. (optional)

Returns

    integer or boolean number of bytes sent, short of the message’s length on a stream socket whose wait ended with part of it queued; false when a send timeout ended the wait with nothing queued.

Raises

Error if the send operation fails or if address parameters are incorrect for the socket type; EAGAIN when the kernel finds no free port to bind an unbound AF_INET or AF_INET6 socket to before it sends.

See also

Usage

-- For a connected TCP socket:
local bytes_sent = tcp_conn_sock:send("Hello, server!")

-- For a UDP socket (sending to 192.168.1.100, port 1234):
local bytes_sent = udp_sock:send("UDP packet", net.aton("192.168.1.100"), 1234)
socket:setsockopt (level, optname, value)
Sets a socket option. SOL_SOCKET options are handled by the network core; any other level is passed to the socket’s protocol handler, mirroring the setsockopt(2) routing.

Parameters

  • level integer option level (e.g., linux.socket.sol.SOCKET).
  • optname integer option name (e.g., linux.socket.so.RCVTIMEO_NEW).
  • value integer or string option value: an integer for the common int payload, from INT_MIN to UINT_MAX, since an unsigned option such as SO_MARK reads those 32 bits as a u32, or a string carrying the option’s packed binary payload.

Raises

Error if the operation fails; unsupported option level on a kernel before 6.7, at a level the socket’s protocol has no setsockopt for.

Usage

local struct = require("struct")
local sk     = require("linux.socket")

-- bound blocking receives to 500 ms (a struct __kernel_sock_timeval)
local timeval = struct(sk.layout.timeval)
sock:setsockopt(sk.sol.SOCKET, sk.so.RCVTIMEO_NEW, timeval:pack(0, 500000))

socket in detail

new (family, type, protocol[, pid])
Creates a new socket object. This function is the primary way to create a socket.

Parameters

  • family integer address family (e.g., linux.socket.af.INET).
  • type integer socket type (e.g., linux.socket.sock.STREAM). linux.socket.sock.PACKET is refused; SOCK_RAW and SOCK_DGRAM replace it on AF_PACKET.
  • protocol integer protocol (e.g., linux.socket.ipproto.TCP). For AF_PACKET sockets, protocol is an ethertype in host byte order, as bind takes it (e.g., linux.eth.proto.ALL for every frame).
  • pid integer a task whose network namespace the socket is created in, instead of the initial one. The pid is read in the initial pid namespace: the number task:pid() returns, whichever task makes the call. The socket holds its network namespace until the kernel frees the socket, which for a TCP connection still shutting down comes after its close, so the namespace outlives the task. The rest of Lunatik (linux.ifindex, netfilter) keeps to the initial network namespace. (optional)

Returns

    socket A new socket object; nil and "ESRCH" if no task has that pid, or the task has exited.

Raises

Error if socket creation fails, “unsupported socket type” for linux.socket.sock.PACKET, “out of bounds” for an argument past an int, an AF_PACKET protocol past 16 bits or a pid outside 1 to PID_MAX_LIMIT, EOPNOTSUPP on a kernel whose sockets cannot hold a namespace of their own, or 'socket': process-context class in interrupt-context runtime in a softirq or hardirq runtime.

See also

Usage

local socket = require("socket")
local sk     = require("linux.socket")
local nl     = require("linux.netlink")

-- TCP/IPv4 socket
local tcp_sock = socket.new(sk.af.INET, sk.sock.STREAM, sk.ipproto.TCP)

-- rtnetlink socket in the network namespace of the task 1234
local rtnl = socket.new(sk.af.NETLINK, sk.sock.RAW, nl.proto.ROUTE, 1234)