- Kernel: Debian
6.1.0-50-amd64(6.1.176-1), QEMU 7.2.22 TCG, two virtual CPUs and 768 MiB RAM; no guest NIC or host disk attached. - Module: built with
W=1, successful MODPOST and no compiler warnings. BTF generation was skipped because a vmlinux debug image was unavailable. - Example: built with
-Wall -Wextra -Werror. - Three complete module load/test/unload cycles passed. Each cycle tested four TAP endpoints and four 128-pair flow sets (IPv4/IPv6, with/without VLAN), both directions, fragments, non-IP traffic, fanout, RX/TX, the actual C worker, no-reader/down/overflow drops, host-path isolation, promiscuity release, failed registration, MTU rollback and deletion during active RX.
- Final markers: three
STEERVTAP_TESTS_PASS, oneSTEERVTAP_QEMU_PASS. No kernel WARNING, BUG, oops, panic or stuck device-reference message. Expected out-of-tree/unsigned-module taint messages are present. - Local artifacts (gitignored):
test-results/qemu.logandtest-results/steervtap-6.1.0-50-amd64.ko.
These are functional checks on a distribution kernel. KASAN/lockdep, physical NIC offloads, five-tuple steering and throughput scaling have not been tested.
Build and run against the same kernel release (validated on 6.1 and 6.8). Do not load a 6.1 module into the Docker Desktop LinuxKit host. Use a disposable Linux VM for runtime tests.
Install matching headers, make, gcc, kmod, iproute2 and Python 3. Kernel options:
CONFIG_TAP, CONFIG_VETH, CONFIG_NET_NS, CONFIG_INET, CONFIG_IPV6,
CONFIG_NET_SCH_INGRESS, CONFIG_NET_CLS_MATCHALL and CONFIG_NET_ACT_GACT.
The tests also require the iproute2 tc command. Root is needed
for module loading, namespaces and temporary character nodes.
make W=1
make tools
sudo modprobe tap
sudo insmod steervtap.ko
sudo python3 tests/integration.py
sudo rmmod steervtapSuccess ends with STEERVTAP_TESTS_PASS. Also review the kernel log for warnings,
oopses, hung tasks and reference-count errors. Tests clean up their namespace
and devices in finally blocks.
Repeat with module unload/reload for lifecycle checking. A separate KASAN/lockdep kernel is required to claim those checks; passing on a distribution kernel is not equivalent.
- Ubuntu 24.04.4 LTS, x86_64, running
6.8.0-101-generic. - Sources and build directory:
/root/steervtap-devon the test server. - Built natively against its installed matching headers with
W=1, successful MODPOST. GCC 13.3.0 has a different Ubuntu package revision from the kernel build compiler, producing a compiler-banner notice; the C source emitted no warnings. BTF generation skipped because vmlinux is unavailable. - Three native load/test/unload cycles passed. The extended TX test wrote 16 uniquely identified frames from each of four TAP FDs per cycle, verified exact frames at the lower veth peer, and checked each endpoint's TX packet and byte counters. RX, fanout, worker echo, drops, host isolation, failed registration, MTU rollback and concurrent deletion also passed.
- The native runner found no new kernel warning/oops and confirmed the module was unloaded, all preexisting link configurations were unchanged, and the default routes were unchanged.
- This test used private network namespaces and veth pairs. It did not bind
the driver's RX handler to the server's management interface. It establishes
native-kernel bidirectional functionality, not physical NIC throughput.
The management interface is a
virtio_netdevice carrying the default route. - Remote evidence:
test-results/native-6.8/{result.json,tests.log,kernel.log}; local copies:test-results/server-6.8/(gitignored).
Reproduce on a Linux test server with no steervtap module already loaded:
sudo make test-native
# Or, after building:
sudo python3 tests/native.py --cycles 3 --output test-results/native-6.8Install qemu-system-x86, busybox-static, cpio, and the Linux image matching
the build headers. This runner constructs a temporary initramfs, boots the
distribution kernel with TCG (no KVM required), and performs three complete
module load/test/unload cycles:
make KDIR=/usr/src/linux-headers-6.1.0-50-amd64
make tools
python3 tests/qemu.py --release 6.1.0-50-amd64It uses no guest NIC, host disks, host module loading or privileged Docker mode.
Logs go to test-results/qemu.log; the runner rejects kernel warnings/oopses
and times out rather than hanging indefinitely. Dependencies must be installed
before running it. QEMU/TAP/namespace tests do not establish KASAN coverage.
Virtual-network functional tests establish routing and basic lifecycle behavior, not physical NIC throughput. Benchmark real hardware with 1/2/4/8 workers, multiple packet sizes, diverse flows and elephant flows. Record PPS, CPU, drops and RX/TX correctness. Address-pair hashing intentionally keeps all connections between one IP pair together; it cannot split one heavy pair.
The configuration suite extends the previous RX/TX checks with empty/clear host pass, CIDR/protocol/port-range rules, first-match pass priority, IPv4/IPv6, VLAN/bidirectional matching, fragment port exclusion, shared group queries, invalid replacement rollback, and concurrent correlated rule/RSS replacements. During concurrent updates, each source must only reach the target paired with its rule; unmatched packets may legitimately pass under the other snapshot. The bounded traffic loop is paced so both snapshots receive traffic.
Both Linux 6.8 native and Linux 6.1 QEMU run this extended suite for three
load/test/unload cycles. Evidence is stored locally under
test-results/config-server-6.8/ and test-results/config-qemu.log.
An additional explicitly authorized test attached one TAP to the server's real
eth0 (virtio_net, the management/default-route interface). Initial client-to-
server UDP probes on port 43987 timed out, without identifying where the packet
was filtered. That attempt restored the NIC and unloaded the module. The
successful test instead used actual replies from the reachable DNS resolver:
- Source
1.1.1.1/32, UDP source port 53, destination ephemeral port 34721. - Empty rules delivered a real DNS response to a host UDP socket.
- A nonmatching source rule also delivered the response to the host.
- The precise matching rule delivered the response to TAP and excluded it from the host socket.
- A complete Ethernet/IP/UDP DNS request written through the TAP FD reached the external resolver; its matching transaction-ID response arrived back on TAP. The virtual interface's TX counter incremented.
- A separate SSH connection succeeded while the rule and TAP were active.
- Clearing restored host DNS delivery. TAP/module removal restored eth0 flags and preserved default routes. No new kernel warning/oops was found.
The opt-in harness is tests/live_eth0.py; it is not run by make test.
It sends five small DNS queries for example.com, uses a dynamically chosen local
UDP port, has a 40-second alarm, and cleans up its own TAP/module in finally.
Run only on the intended Linux test server from the repository root with the
matching module built and no steervtap module already loaded:
sudo python3 tests/live_eth0.pyIt prints SSH_WINDOW for a five-second active window in which a caller can
independently probe SSH. Results: test-results/live-eth0.json and
test-results/live-eth0-kernel.log; local copies accompany the native evidence.
This establishes functional ingress steering and external TX through this cloud
NIC, not bare-metal hardware RSS or throughput performance.
The receive-path revision returns selected unicast to the core with
RX_HANDLER_ANOTHER and queues multicast copies through netif_rx. Integration
checks now attach AF_PACKET sockets to each virtual device (the receive hook used
by tcpdump) and verify that the selected reader and capture socket both receive
unicast, while all four readers/capture sockets receive broadcast copies.
A tc clsact/matchall action drop check proves the packet traverses virtual
interface ingress: AF_PACKET sees it, but the TAP reader does not. Removing the
filter restores reader delivery. The QEMU image now includes tc and the
sch_ingress, cls_matchall and act_gact modules and their dependencies.
The ingress revision subsequently passed three full native cycles on Linux 6.8.0-101-generic and three QEMU cycles on Linux 6.1.0-50-amd64, including the AF_PACKET and tc ingress checks above. It was loaded on the cloud server, preserving the two-member eth0 group and its narrow DNS rule. Actual tcpdump captures on the virtual interfaces recorded five DNS responses on one hash-selected member, zero on the other, and zero capture drops. The default route remained unchanged. TAP FD writes still transmit on the lower NIC.
Evidence from that upgrade is under
test-results/ingress-upgrade-20260911-211926/native/ and
test-results/ingress-upgrade-20260911-212012/ on the test server; local QEMU
evidence is test-results/ingress-qemu.log. Runtime interface names, addresses
and worker processes are deployment state, not part of the source repository.