Skip to content

Latest commit

 

History

History
181 lines (150 loc) · 9.21 KB

File metadata and controls

181 lines (150 loc) · 9.21 KB

Linux validation

Recorded validation: 2026-09-11

  • 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, one STEERVTAP_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.log and test-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.

Reproduce on Linux

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 steervtap

Success 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.

Native server validation: 2026-09-11

  • Ubuntu 24.04.4 LTS, x86_64, running 6.8.0-101-generic.
  • Sources and build directory: /root/steervtap-dev on 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_net device 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.8

Isolated QEMU validation from a Linux build container

Install 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-amd64

It 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.

Atomic configuration and live eth0 validation: 2026-09-11

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.py

It 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.

Virtual-device ingress regression

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.