Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ alpine.ext4
deps/
lkl-x86_64/
lkl-aarch64/
externals/minislirp/
target/
tests/guest/*-test
!tests/guest/*-test.c
Expand Down
3 changes: 0 additions & 3 deletions .gitmodules

This file was deleted.

24 changes: 22 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ LDLIBS = -llkl -lpthread -ldl -lm -lrt
# Optional: SLIRP networking (set KBOX_HAS_SLIRP=1 to enable)
ifdef KBOX_HAS_SLIRP
SLIRP_DIR = externals/minislirp
SLIRP_HDR = $(SLIRP_DIR)/src/libslirp.h
CFLAGS += -DKBOX_HAS_SLIRP -I$(SLIRP_DIR)/src
SLIRP_SRCS = $(wildcard $(SLIRP_DIR)/src/*.c)
SLIRP_OBJS = $(SLIRP_SRCS:.c=.o)
Expand Down Expand Up @@ -110,7 +111,7 @@ ROOTFS = alpine.ext4

# ---- Top-level targets ----

.PHONY: all clean check check-unit check-integration check-stress guest-bins stress-bins rootfs fetch-lkl install-hooks web-assets
.PHONY: all clean check check-unit check-integration check-stress guest-bins stress-bins rootfs fetch-lkl fetch-minislirp install-hooks web-assets

all: $(TARGET)
ifneq ($(wildcard .git),)
Expand All @@ -132,6 +133,25 @@ $(LKL_LIB):
@echo "LKL library not found at $(LKL_DIR). Fetching..."
./scripts/fetch-lkl.sh

# Auto-fetch minislirp if missing (shallow clone, no submodule).
# $(wildcard) evaluates at parse time, so if minislirp has not been
# fetched yet SLIRP_SRCS is empty. Guard: fetch and re-exec make so
# the wildcard picks up the newly-cloned sources.
ifdef KBOX_HAS_SLIRP
# Auto-fetch minislirp if the header is missing and a build target
# was requested (skip for clean/fetch-only goals).
ifneq ($(filter clean,$(MAKECMDGOALS)),clean)
ifeq ($(wildcard $(SLIRP_HDR)),)
$(shell ./scripts/fetch-minislirp.sh >&2)
Comment thread
cubic-dev-ai[bot] marked this conversation as resolved.
SLIRP_SRCS = $(wildcard $(SLIRP_DIR)/src/*.c)
SLIRP_OBJS = $(SLIRP_SRCS:.c=.o)
endif
endif

fetch-minislirp:
./scripts/fetch-minislirp.sh
endif

# ---- Test targets ----

check: check-unit check-integration check-stress
Expand Down Expand Up @@ -211,7 +231,7 @@ $(SRC_DIR)/cli.o: include/kbox/cli.h
$(SRC_DIR)/probe.o: include/kbox/probe.h include/kbox/seccomp-defs.h
$(SRC_DIR)/image.o: include/kbox/image.h include/kbox/lkl-wrap.h include/kbox/mount.h include/kbox/net.h include/kbox/identity.h include/kbox/probe.h include/kbox/seccomp.h
$(SRC_DIR)/shadow-fd.o: include/kbox/shadow-fd.h include/kbox/lkl-wrap.h include/kbox/syscall-nr.h
$(SRC_DIR)/seccomp-dispatch.o: include/kbox/seccomp.h include/kbox/seccomp-defs.h include/kbox/fd-table.h include/kbox/lkl-wrap.h include/kbox/procmem.h include/kbox/path.h include/kbox/identity.h include/kbox/shadow-fd.h
$(SRC_DIR)/seccomp-dispatch.o: include/kbox/seccomp.h include/kbox/seccomp-defs.h include/kbox/fd-table.h include/kbox/lkl-wrap.h include/kbox/procmem.h include/kbox/path.h include/kbox/identity.h include/kbox/shadow-fd.h include/kbox/net.h
$(SRC_DIR)/seccomp-supervisor.o: include/kbox/seccomp.h include/kbox/seccomp-defs.h include/kbox/syscall-nr.h
$(SRC_DIR)/seccomp-bpf.o: include/kbox/seccomp.h include/kbox/seccomp-defs.h include/kbox/syscall-nr.h
$(SRC_DIR)/seccomp-notify.o: include/kbox/seccomp.h include/kbox/seccomp-defs.h
Expand Down
10 changes: 7 additions & 3 deletions include/kbox/fd-table.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,16 @@ struct kbox_sysnrs; /* forward declaration */

#define KBOX_FD_BASE 32768
#define KBOX_FD_TABLE_MAX 4096
#define KBOX_LOW_FD_MAX 1024 /* redirect slots for FDs 0..1023 (dup2 targets) \
*/
#define KBOX_LOW_FD_MAX \
1024 /* redirect slots for FDs 0..1023 (dup2 targets) \
*/

struct kbox_fd_entry {
long lkl_fd; /* LKL-internal FD, -1 if slot is free */
long host_fd; /* host memfd shadow, -1 if none */
long host_fd; /* host memfd shadow / tracee FD number, -1 if none */
int shadow_sp; /* supervisor's dup of shadow socket sp[1], -1 if none.
* Kept alive so dup/dup2/dup3 can inject new copies
* into the tracee via ADDFD. */
int mirror_tty; /* 1 if this FD mirrors a host TTY */
int cloexec; /* O_CLOEXEC tracking */
};
Expand Down
99 changes: 99 additions & 0 deletions include/kbox/lkl-wrap.h
Original file line number Diff line number Diff line change
Expand Up @@ -318,4 +318,103 @@ long kbox_lkl_utimensat(const struct kbox_sysnrs *s,
const void *times,
long flags);

/* --- Socket wrappers --- */

long kbox_lkl_bind(const struct kbox_sysnrs *s,
long fd,
const void *addr,
long addrlen);
long kbox_lkl_getsockopt(const struct kbox_sysnrs *s,
long fd,
long level,
long optname,
void *optval,
void *optlen);
long kbox_lkl_setsockopt(const struct kbox_sysnrs *s,
long fd,
long level,
long optname,
const void *optval,
long optlen);
long kbox_lkl_getsockname(const struct kbox_sysnrs *s,
long fd,
void *addr,
void *addrlen);
long kbox_lkl_getpeername(const struct kbox_sysnrs *s,
long fd,
void *addr,
void *addrlen);
long kbox_lkl_shutdown(const struct kbox_sysnrs *s, long fd, long how);
long kbox_lkl_sendto(const struct kbox_sysnrs *s,
long fd,
const void *buf,
long len,
long flags,
const void *addr,
long addrlen);
long kbox_lkl_recvfrom(const struct kbox_sysnrs *s,
long fd,
void *buf,
long len,
long flags,
void *addr,
void *addrlen);

/* Forward declarations for netdev ops. */
struct iovec;
struct lkl_netdev;

/* --- LKL network device FFI --- */

/*
* LKL virtio-net device operations. Must match LKL's struct lkl_dev_net_ops
* in tools/lkl/include/lkl_host.h. We declare a compatible struct rather
* than pulling in the full LKL headers.
*
* iov-based TX/RX: each callback receives a scatter/gather array.
* poll: returns a bitmask of LKL_DEV_NET_POLL_{RX,TX,HUP}.
* poll_hup: wakes the poll callback (e.g. write a byte to a wakeup pipe).
* free: cleanup on device removal.
*/
struct lkl_dev_net_ops {
int (*tx)(struct lkl_netdev *nd, struct iovec *iov, int cnt);
int (*rx)(struct lkl_netdev *nd, struct iovec *iov, int cnt);
int (*poll)(struct lkl_netdev *nd);
void (*poll_hup)(struct lkl_netdev *nd);
void (*free)(struct lkl_netdev *nd);
};

struct lkl_netdev {
struct lkl_dev_net_ops *ops;
int id;
int has_vnet_hdr;
unsigned char mac[6];
};

struct lkl_netdev_args {
unsigned char mac[6];
unsigned offload;
};

#define LKL_DEV_NET_POLL_RX 1
#define LKL_DEV_NET_POLL_TX 2
#define LKL_DEV_NET_POLL_HUP 4

extern int lkl_netdev_add(struct lkl_netdev *nd, struct lkl_netdev_args *args);
extern int lkl_netdev_get_ifindex(int id);
extern int lkl_if_up(int ifindex);
extern int lkl_if_set_ipv4(int ifindex,
unsigned int addr,
unsigned int netmask_len);
extern int lkl_set_ipv4_gateway(unsigned int addr);
extern int lkl_if_add_linklocal(int ifindex,
int af,
void *addr,
int netprefix_len);
extern int lkl_if_add_gateway(int ifindex, int af, void *gwaddr);
extern int lkl_if_set_ipv4_gateway(int ifindex,
unsigned int src_addr,
unsigned int src_masklen,
unsigned int via_addr);

#endif /* KBOX_LKL_WRAP_H */
45 changes: 39 additions & 6 deletions include/kbox/net.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,17 +24,25 @@
#include "kbox/syscall-nr.h"

/*
* Initialize SLIRP networking.
* Register the LKL virtio-net device and start SLIRP.
*
* Creates the LKL virtio-net device, starts the SLIRP instance,
* configures the guest interface (IP, route, DNS).
* Must be called BEFORE lkl_start_kernel because LKL probes
* netdev during boot. Creates pipes, SLIRP instance, event loop
* thread, and registers the netdev with LKL.
*
* Must be called after kernel boot and sysnrs detection,
* before the supervisor fork.
* Returns 0 on success, -1 on error.
*/
int kbox_net_add_device(void);

/*
* Configure the guest network interface.
*
* Must be called AFTER kernel boot and sysnrs detection.
* Brings the interface up, sets IP/gateway/DNS.
*
* Returns 0 on success, -1 on error.
*/
int kbox_net_init(const struct kbox_sysnrs *sysnrs);
int kbox_net_configure(const struct kbox_sysnrs *sysnrs);

/*
* Tear down SLIRP networking.
Expand All @@ -44,4 +52,29 @@ int kbox_net_init(const struct kbox_sysnrs *sysnrs);
*/
void kbox_net_cleanup(void);

/*
* Register a shadow socket with the SLIRP event loop.
*
* The event loop pumps data between supervisor_fd (one end of a
* socketpair visible to the supervisor) and lkl_fd (the LKL-side
* socket). sock_type is SOCK_STREAM or SOCK_DGRAM.
*
* Returns 0 on success, -1 on error.
*/
/*
* Returns 1 if SLIRP networking is initialized and active.
*/
int kbox_net_is_active(void);

int kbox_net_register_socket(int lkl_fd, int supervisor_fd, int sock_type);

/*
* Deregister a shadow socket from the SLIRP event loop.
*
* Called when the tracee closes a shadow socket FD.
* Matches by LKL FD since the supervisor_fd is internal
* to the event loop.
*/
void kbox_net_deregister_socket(int lkl_fd);

#endif /* KBOX_NET_H */
8 changes: 8 additions & 0 deletions include/kbox/seccomp.h
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,14 @@ int kbox_notify_addfd(int listener_fd,
int srcfd,
uint32_t newfd_flags);

/* Like kbox_notify_addfd but installs the FD at a specific number (for
* dup2/dup3). */
int kbox_notify_addfd_at(int listener_fd,
uint64_t id,
int srcfd,
int target_fd,
uint32_t newfd_flags);

/* --- Dispatch (seccomp-dispatch.c) --- */

/*
Expand Down
4 changes: 4 additions & 0 deletions include/kbox/syscall-nr.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ struct kbox_sysnrs {

/* File I/O */
long openat, openat2, fcntl, socket, connect;
long bind, sendto, recvfrom, sendmsg, recvmsg;
long getsockopt, setsockopt, getsockname, getpeername, shutdown;

/* FD manipulation */
long dup, dup3, close;
Expand Down Expand Up @@ -87,6 +89,8 @@ struct kbox_host_nrs {
int fchmodat, fchownat;
int close;
int sendmsg, socket, connect, bind, listen, accept, accept4;
int sendto, recvfrom, recvmsg;
int getsockopt, setsockopt, getsockname, getpeername, shutdown;
int exit, exit_group;
int fcntl, dup, dup2, dup3;
int read, write, pread64, lseek;
Expand Down
31 changes: 31 additions & 0 deletions scripts/fetch-minislirp.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
#!/bin/sh
# SPDX-License-Identifier: MIT
# Fetch minislirp via shallow clone for SLIRP networking support.
#
# Usage: ./scripts/fetch-minislirp.sh
# Override SLIRP_DIR to change output directory.
# Override MINISLIRP_REPO to use a fork.

set -eu

SLIRP_DIR="${SLIRP_DIR:-externals/minislirp}"
REPO="${MINISLIRP_REPO:-https://github.com/sysprog21/minislirp}"

# Validate SLIRP_DIR to prevent rm -rf on unintended paths.
case "$SLIRP_DIR" in
/*|""|.|..|*/..*) echo "error: SLIRP_DIR must be a relative sub-path without .." >&2; exit 1 ;;
esac

if [ -f "${SLIRP_DIR}/src/libslirp.h" ]; then
echo "minislirp already present at ${SLIRP_DIR}"
exit 0
fi

echo "Fetching minislirp from ${REPO} ..."
rm -rf "${SLIRP_DIR}"

@cubic-dev-ai cubic-dev-ai Bot Mar 20, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: Validate SLIRP_DIR before rm -rf to avoid deleting unintended paths when the override is misconfigured.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At scripts/fetch-minislirp.sh, line 20:

<comment>Validate `SLIRP_DIR` before `rm -rf` to avoid deleting unintended paths when the override is misconfigured.</comment>

<file context>
@@ -0,0 +1,26 @@
+fi
+
+echo "Fetching minislirp from ${REPO} ..."
+rm -rf "${SLIRP_DIR}"
+git clone --depth=1 "${REPO}" "${SLIRP_DIR}"
+
</file context>
Fix with Cubic

git clone --depth=1 "${REPO}" "${SLIRP_DIR}"

# Strip git metadata -- we don't need history.
rm -rf "${SLIRP_DIR}/.git"

echo "minislirp ready at ${SLIRP_DIR}"
4 changes: 4 additions & 0 deletions scripts/pre-commit.hook
Original file line number Diff line number Diff line change
Expand Up @@ -56,10 +56,14 @@ cppcheck_suppressions() {
"unusedFunction"
"syntaxError"
"constParameterPointer"
"constVariablePointer"
"unusedStructMember"
"redundantAssignment"
"staticFunction"
"checkLevelNormal"
"variableScope"
"compareValueOutOfTypeRangeError"
"constVariable"
)

local out="--inline-suppr "
Expand Down
20 changes: 17 additions & 3 deletions scripts/run-tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -269,17 +269,31 @@ echo "--- Networking ---"

# Check if kbox was built with SLIRP support by testing --net flag.
if "$KBOX" image -S "$ROOTFS" --net -- /bin/true 2> /dev/null; then
for test_prog in net-dns-test; do
if "$KBOX" image -S "$ROOTFS" --net -- /bin/sh -c "test -x /opt/tests/${test_prog}" \
2> /dev/null; then
expect_success "$test_prog" \
"$KBOX" image -S "$ROOTFS" --net -- "/opt/tests/${test_prog}"
else
printf " %-40s ${YELLOW}SKIP${NC} (not in rootfs)\n" "$test_prog"
SKIP=$((SKIP + 1))
fi
done

expect_output "net-ping-gateway" "bytes from" \
"$KBOX" image -S "$ROOTFS" --net -- /bin/sh -c "ping -c 1 -W 3 10.0.2.2"

expect_output "net-resolv-conf" "nameserver" \
"$KBOX" image -S "$ROOTFS" --net -- /bin/sh -c "cat /etc/resolv.conf"

# wget test (outbound TCP via SLIRP)
expect_success "net-wget-external" \
"$KBOX" image -S "$ROOTFS" --net -- /bin/sh -c "wget -q -O /dev/null http://httpbin.org/get"
# Check that DNS resolves and TCP connects. The HTTP response may
# fail (busybox wget vs chunked encoding / virtual hosting), so
# we check for the "Connecting to" line which proves DNS + TCP.
expect_output "net-wget-external" "Connecting to" \
"$KBOX" image -S "$ROOTFS" --net -- /bin/sh -c "wget -S -O /dev/null http://www.google.com/ 2>&1 || true"
else
for t in net-ping-gateway net-resolv-conf net-wget-external; do
for t in net-dns-test net-ping-gateway net-resolv-conf net-wget-external; do
printf " %-40s ${YELLOW}SKIP${NC} (no SLIRP support)\n" "$t"
SKIP=$((SKIP + 1))
done
Expand Down
Loading
Loading