Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
ccb09b1
tests/run.sh: Add macOS cross-compilation support for guest-agent
mtjhrc Feb 2, 2026
5bb56f5
Makefile: add macOS support to test target
mtjhrc Feb 2, 2026
d84eeaa
tests/README.md: document running tests on macOS
mtjhrc Feb 2, 2026
5e75488
tests: Add support for skipping tests
mtjhrc Feb 2, 2026
8ae32b6
tests: Fix TcpTester::run_server closing TCP stream too early
mtjhrc Feb 11, 2026
03d29c1
tests: Explicitly specify a /tmp directory
mtjhrc Feb 12, 2026
a78aebf
net: add a new flag NET_FLAG_INCLUDE_VNET_HEADER
akerouanton Dec 17, 2025
3b69f88
examples/chroot_vm: Add ability to attach a TAP for networking
mtjhrc Feb 4, 2026
34a23cd
utils: Introduce a set_non_blocking method for file descriptors
mtjhrc Feb 4, 2026
c777eb1
tests: Introduce virtio-net tests (tap, passt, gvproxy)
mtjhrc Feb 4, 2026
b9df69a
fixup: (different branch) do not run unit tests on macos
mtjhrc Feb 11, 2026
126e26d
fixup! tests: Introduce virtio-net tests (tap, passt, gvproxy)
mtjhrc Feb 16, 2026
35fad10
tests: WIP introduce performance tests
mtjhrc Feb 16, 2026
906f470
fixup: vmnet helper test
mtjhrc Feb 24, 2026
cd9a3bc
fixup tests
mtjhrc Feb 24, 2026
3d0210a
CI: run NET tests
mtjhrc Feb 24, 2026
3a8db91
tests: Add vmnet-helper tests
mtjhrc Feb 16, 2026
7107ff0
devices/queue: Add Debug impl for DescriptorChain
mtjhrc Feb 24, 2026
11298b1
devices: Introduce test_utils for creating driving virtio queues
mtjhrc Feb 24, 2026
7c80d97
devices: Introduce batch_queue utilities: TxQueueConsumer and RxQueue…
mtjhrc Feb 24, 2026
4490a24
devices/net: Rewrite virtio-net in terms of new batch_queue utilities
mtjhrc Feb 24, 2026
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
40 changes: 20 additions & 20 deletions .github/workflows/integration_tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,23 +20,23 @@ jobs:
- name: Clippy (test_cases guest)
run: |
cd tests
cargo clippy --locked -p test_cases --features guest -- -D warnings
IPERF_DURATION=15 cargo clippy --locked -p test_cases --features guest -- -D warnings

- name: Clippy (test_cases host)
run: |
cd tests
PKG_CONFIG_PATH="$(realpath ../test-prefix/lib64/pkgconfig/)" LD_LIBRARY_PATH="$(realpath ../test-prefix/lib64/)" cargo clippy --locked -p test_cases --features host -- -D warnings
IPERF_DURATION=15 PKG_CONFIG_PATH="$(realpath ../test-prefix/lib64/pkgconfig/)" LD_LIBRARY_PATH="$(realpath ../test-prefix/lib64/)" cargo clippy --locked -p test_cases --features host -- -D warnings

- name: Clippy (runner)
run: |
cd tests
PKG_CONFIG_PATH="$(realpath ../test-prefix/lib64/pkgconfig/)" LD_LIBRARY_PATH="$(realpath ../test-prefix/lib64/)" cargo clippy --locked -p runner -- -D warnings

- name: Clippy (guest-agent)
run: |
cd tests
cargo clippy --locked --target x86_64-unknown-linux-musl -p guest-agent -- -D warnings

- name: Enable KVM group perms
run: |
echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules
Expand All @@ -45,13 +45,13 @@ jobs:
sudo usermod -a -G kvm $USER

- name: Install additional packages
run: sudo apt-get install -y --no-install-recommends build-essential patchelf pkg-config net-tools
run: sudo apt-get install -y --no-install-recommends build-essential patchelf pkg-config net-tools iperf3

- name: Install libkrunfw
run: TAG=`curl -sL https://api.github.com/repos/containers/libkrunfw/releases/latest |jq -r .tag_name` && curl -L -o /tmp/libkrunfw-x86_64.tgz https://github.com/containers/libkrunfw/releases/download/$TAG/libkrunfw-x86_64.tgz && mkdir tmp && tar xf /tmp/libkrunfw-x86_64.tgz -C tmp && sudo mv tmp/lib64/* /lib/x86_64-linux-gnu

- name: Integration tests
run: KRUN_ENOMEM_WORKAROUND=1 KRUN_NO_UNSHARE=1 KRUN_TEST_BASE_DIR=/tmp/libkrun-tests make test TEST_FLAGS="--keep-all --github-summary"
run: KRUN_ENOMEM_WORKAROUND=1 KRUN_NO_UNSHARE=1 KRUN_TEST_BASE_DIR=/tmp/libkrun-tests make test NET=1 IPERF_DURATION=30 TEST_FLAGS="--keep-all --github-summary"

- name: Upload test logs
if: always()
Expand Down Expand Up @@ -81,34 +81,34 @@ jobs:
- name: Clippy (test_cases guest)
run: |
cd tests
cargo clippy --locked -p test_cases --features guest -- -D warnings
IPERF_DURATION=15 cargo clippy --locked -p test_cases --features guest -- -D warnings

- name: Clippy (test_cases host)
run: |
cd tests
PKG_CONFIG_PATH="$(realpath ../test-prefix/lib64/pkgconfig/)" LD_LIBRARY_PATH="$(realpath ../test-prefix/lib64/)" cargo clippy --locked -p test_cases --features host -- -D warnings
IPERF_DURATION=15 PKG_CONFIG_PATH="$(realpath ../test-prefix/lib64/pkgconfig/)" LD_LIBRARY_PATH="$(realpath ../test-prefix/lib64/)" cargo clippy --locked -p test_cases --features host -- -D warnings

- name: Clippy (runner)
run: |
cd tests
PKG_CONFIG_PATH="$(realpath ../test-prefix/lib64/pkgconfig/)" LD_LIBRARY_PATH="$(realpath ../test-prefix/lib64/)" cargo clippy --locked -p runner -- -D warnings

- name: Clippy (guest-agent)
run: |
cd tests
cargo clippy --locked --target aarch64-unknown-linux-musl -p guest-agent -- -D warnings

- name: Install additional packages
run: sudo apt-get install -y --no-install-recommends build-essential patchelf pkg-config net-tools
run: sudo apt-get install -y --no-install-recommends build-essential patchelf pkg-config net-tools iperf3

- name: Install libkrunfw
run: TAG=`curl -sL https://api.github.com/repos/containers/libkrunfw/releases/latest |jq -r .tag_name` && curl -L -o /tmp/libkrunfw-aarch64.tgz https://github.com/containers/libkrunfw/releases/download/$TAG/libkrunfw-aarch64.tgz && mkdir tmp && tar xf /tmp/libkrunfw-aarch64.tgz -C tmp && sudo mv tmp/lib64/* /lib/aarch64-linux-gnu

- name: Clean up tests directory
run: rm -fr /tmp/libkrun-tests

- name: Integration tests
run: KRUN_ENOMEM_WORKAROUND=1 KRUN_NO_UNSHARE=1 KRUN_TEST_BASE_DIR=/tmp/libkrun-tests make test TEST_FLAGS="--keep-all --github-summary"
run: KRUN_ENOMEM_WORKAROUND=1 KRUN_NO_UNSHARE=1 KRUN_TEST_BASE_DIR=/tmp/libkrun-tests make test NET=1 IPERF_DURATION=30 TEST_FLAGS="--keep-all --github-summary"

- name: Upload test logs
if: always()
Expand Down
13 changes: 10 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -232,14 +232,21 @@ clean:

clean-all: clean clean-sysroot

test-prefix/lib64/libkrun.pc: $(LIBRARY_RELEASE_$(OS))
test-prefix/$(LIBDIR_$(OS))/libkrun.pc: $(LIBRARY_RELEASE_$(OS))
mkdir -p test-prefix
PREFIX="$$(realpath test-prefix)" make install

test-prefix: test-prefix/lib64/libkrun.pc
test-prefix: test-prefix/$(LIBDIR_$(OS))/libkrun.pc

TEST ?= all
TEST_FLAGS ?=

# Library path variable differs by OS
LIBPATH_VAR_Linux = LD_LIBRARY_PATH
LIBPATH_VAR_Darwin = DYLD_LIBRARY_PATH
# Extra library paths needed for tests (libkrunfw, llvm)
EXTRA_LIBPATH_Linux =
EXTRA_LIBPATH_Darwin = /opt/homebrew/opt/libkrunfw/lib:/opt/homebrew/opt/llvm/lib

test: test-prefix
cd tests; RUST_LOG=trace LD_LIBRARY_PATH="$$(realpath ../test-prefix/lib64/)" PKG_CONFIG_PATH="$$(realpath ../test-prefix/lib64/pkgconfig/)" ./run.sh test --test-case "$(TEST)" $(TEST_FLAGS)
cd tests; RUST_LOG=trace $(LIBPATH_VAR_$(OS))="$$(realpath ../test-prefix/$(LIBDIR_$(OS))/):$(EXTRA_LIBPATH_$(OS)):$${$(LIBPATH_VAR_$(OS))}" PKG_CONFIG_PATH="$$(realpath ../test-prefix/$(LIBDIR_$(OS))/pkgconfig/)" ./run.sh test --test-case "$(TEST)" $(TEST_FLAGS)
39 changes: 31 additions & 8 deletions examples/chroot_vm.c
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
enum net_mode {
NET_MODE_PASST = 0,
NET_MODE_TSI,
NET_MODE_TAP,
};

static void print_help(char *const name)
Expand All @@ -38,8 +39,9 @@ static void print_help(char *const name)
" --log=PATH Write libkrun log to file or named pipe at PATH\n"
" --color-log=PATH Write libkrun log to file or named pipe at PATH, use color\n"
" --net=NET_MODE Set network mode\n"
" --passt-socket=PATH Instead of starting passt, connect to passt socket at PATH"
"NET_MODE can be either TSI (default) or PASST\n"
" --passt-socket=PATH Instead of starting passt, connect to passt socket at PATH\n"
" --tap=NAME Use TAP device NAME for networking\n"
"NET_MODE can be TSI (default), PASST, or TAP\n"
"\n"
"NEWROOT: the root directory of the vm\n"
"COMMAND: the command you want to execute in the vm\n"
Expand All @@ -54,6 +56,7 @@ static const struct option long_options[] = {
{ "color-log", required_argument, NULL, 'C' },
{ "net_mode", required_argument, NULL, 'N' },
{ "passt-socket", required_argument, NULL, 'P' },
{ "tap", required_argument, NULL, 'T' },
{ NULL, 0, NULL, 0 }
};

Expand All @@ -63,6 +66,7 @@ struct cmdline {
uint32_t log_style;
enum net_mode net_mode;
char const *passt_socket_path;
char const *tap_name;
char const *new_root;
char *const *guest_argv;
};
Expand All @@ -89,6 +93,7 @@ bool parse_cmdline(int argc, char *const argv[], struct cmdline *cmdline)
.show_help = false,
.net_mode = NET_MODE_TSI,
.passt_socket_path = NULL,
.tap_name = NULL,
.new_root = NULL,
.guest_argv = NULL,
.log_target = KRUN_LOG_TARGET_DEFAULT,
Expand Down Expand Up @@ -116,6 +121,8 @@ bool parse_cmdline(int argc, char *const argv[], struct cmdline *cmdline)
cmdline->net_mode = NET_MODE_TSI;
} else if(strcasecmp("PASST", optarg) == 0) {
cmdline->net_mode = NET_MODE_PASST;
} else if(strcasecmp("TAP", optarg) == 0) {
cmdline->net_mode = NET_MODE_TAP;
} else {
fprintf(stderr, "Unknown mode %s\n", optarg);
return false;
Expand All @@ -124,6 +131,10 @@ bool parse_cmdline(int argc, char *const argv[], struct cmdline *cmdline)
case 'P':
cmdline->passt_socket_path = optarg;
break;
case 'T':
cmdline->tap_name = optarg;
cmdline->net_mode = NET_MODE_TAP;
break;
case '?':
return false;
default:
Expand Down Expand Up @@ -268,15 +279,18 @@ int main(int argc, char *const argv[])
return -1;
}

// Map port 18000 in the host to 8000 in the guest (if networking uses TSI)
if (cmdline.net_mode == NET_MODE_TSI) {
// Configure networking based on mode
uint8_t mac[] = {0x5a, 0x94, 0xef, 0xe4, 0x0c, 0xee};
switch (cmdline.net_mode) {
case NET_MODE_TSI:
// Map port 18000 in the host to 8000 in the guest
if (err = krun_set_port_map(ctx_id, &port_map[0])) {
errno = -err;
perror("Error configuring port map");
return -1;
}
} else {
uint8_t mac[] = {0x5a, 0x94, 0xef, 0xe4, 0x0c, 0xee};
break;
case NET_MODE_PASST:
if (cmdline.passt_socket_path != NULL) {
if (err = krun_add_net_unixstream(ctx_id, cmdline.passt_socket_path, -1, &mac[0], COMPAT_NET_FEATURES, 0)) {
errno = -err;
Expand All @@ -285,17 +299,26 @@ int main(int argc, char *const argv[])
}
} else {
int passt_fd = start_passt();

if (passt_fd < 0) {
return -1;
}

if (err = krun_add_net_unixstream(ctx_id, NULL, passt_fd, &mac[0], COMPAT_NET_FEATURES, 0)) {
errno = -err;
perror("Error configuring net mode");
return -1;
}
}
break;
case NET_MODE_TAP:
if (cmdline.tap_name == NULL) {
return -1;
}
if (err = krun_add_net_tap(ctx_id, (char *)cmdline.tap_name, &mac[0], COMPAT_NET_FEATURES, 0)) {
errno = -err;
perror("Error configuring TAP network");
return -1;
}
break;
}

// Configure the rlimits that will be set in the guest
Expand Down
Binary file added examples/chroot_vm_test
Binary file not shown.
67 changes: 67 additions & 0 deletions examples/test_tap_net.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
#!/bin/bash
# Test script for virtio-net with tap backend
# This script builds libkrun, compiles chroot_vm, and runs a network test

set -e

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
PREFIX="$SCRIPT_DIR/libkrun-prefix"
CHROOT_ROOT="${CHROOT_ROOT:-/home/mhrica/c/my_rootfs2}"
TAP_DEVICE="${TAP_DEVICE:-tap0}"

echo "=== libkrun tap network test ==="
echo "Project root: $PROJECT_ROOT"
echo "Prefix: $PREFIX"
echo "Chroot root: $CHROOT_ROOT"
echo "TAP device: $TAP_DEVICE"
echo ""

# Build and install libkrun
cd "$PROJECT_ROOT"

if [[ "$1" == "--rebuild" ]] || [[ ! -f "$PREFIX/lib64/libkrun.so" ]]; then
echo "=== Building libkrun ==="
make clean 2>/dev/null || true
make PREFIX="$PREFIX"
make install PREFIX="$PREFIX"
fi

# Compile chroot_vm
echo "=== Compiling chroot_vm_test ==="
cd "$SCRIPT_DIR"
PKG_CONFIG_PATH="$PREFIX/lib64/pkgconfig" \
gcc -g -o chroot_vm_test chroot_vm.c $(pkg-config --cflags --libs libkrun)

# Create a named pipe for log output
LOG_PIPE="$SCRIPT_DIR/krun_log_pipe"
rm -f "$LOG_PIPE"
mkfifo "$LOG_PIPE"

# Start log reader in background
echo "=== Starting log reader ==="
cat "$LOG_PIPE" &
LOG_PID=$!

# Cleanup on exit
cleanup() {
kill $LOG_PID 2>/dev/null || true
rm -f "$LOG_PIPE"
}
trap cleanup EXIT

# Run the test
echo "=== Running chroot_vm with tap backend ==="
echo "Command: LD_LIBRARY_PATH=$PREFIX/lib64 ./chroot_vm_test --color-log=$LOG_PIPE --net=TAP --tap=$TAP_DEVICE $CHROOT_ROOT /usr/bin/ping -c 3 8.8.8.8"
echo ""

LD_LIBRARY_PATH="$PREFIX/lib64" \
./chroot_vm_test \
--color-log="$LOG_PIPE" \
--net=TAP \
--tap="$TAP_DEVICE" \
"$CHROOT_ROOT" \
/guest_net_test.sh

echo ""
echo "=== Test complete ==="
1 change: 1 addition & 0 deletions include/libkrun.h
Original file line number Diff line number Diff line change
Expand Up @@ -326,6 +326,7 @@ int32_t krun_add_virtiofs2(uint32_t ctx_id,
/* Send the VFKIT magic after establishing the connection,
as required by gvproxy in vfkit mode. */
#define NET_FLAG_VFKIT 1 << 0
#define NET_FLAG_INCLUDE_VNET_HEADER 1 << 1

/* TSI (Transparent Socket Impersonation) feature flags for vsock */
#define KRUN_TSI_HIJACK_INET (1 << 0)
Expand Down
5 changes: 3 additions & 2 deletions src/devices/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ edition = "2021"
tee = []
amd-sev = ["blk", "tee"]
tdx = ["blk", "tee"]
net = []
net = ["batch_queue"]
batch_queue = []
blk = []
efi = ["blk", "net"]
gpu = ["rutabaga_gfx", "thiserror", "zerocopy", "krun_display"]
Expand All @@ -24,7 +25,7 @@ crossbeam-channel = ">=0.5.15"
libc = ">=0.2.39"
libloading = "0.8"
log = "0.4.0"
nix = { version = "0.30.1", features = ["ioctl", "net", "poll", "socket", "fs"] }
nix = { version = "0.30.1", features = ["ioctl", "net", "poll", "socket", "fs", "uio"] }
pw = { package = "pipewire", version = "0.8.0", optional = true }
rand = "0.9.2"
thiserror = { version = "2.0", optional = true }
Expand Down
Loading
Loading