Skip to content

feat(wireguard): add automatic userspace fallback - #199

Merged
zyjhtangtang merged 5 commits into
openyurtio:mainfrom
Jonny-ee:feat/wireguard-go
Oct 12, 2026
Merged

zyjhtangtang merged 5 commits into
openyurtio:mainfrom
Jonny-ee:feat/wireguard-go

Conversation

@Jonny-ee

Copy link
Copy Markdown

Description

Fixes #193

Enable Raven to establish WireGuard tunnels on nodes without kernel WireGuard support by automatically falling back to wireguard-go.

The existing wireguard driver remains kernel-first. Fallback occurs only when kernel device creation returns EOPNOTSUPP/ENOTSUP. No new public configuration or CRD changes are required.

Changes Made

  • Build and bundle the pinned wireguard-go version in the agent image.
  • Manage userspace process startup, health checks, recovery, and cleanup through the existing Engine reconciliation queue.
  • Restore peers after userspace failures while preserving the current driver's in-memory key.
  • Fix reuse of unchanged VXLAN devices during repeated reconciliation to allow network recovery.

See the design document for implementation details, compatibility considerations, and completed validation.

- Fall back to wireguard-go when kernel WireGuard is unsupported
- Manage userspace process recovery and cleanup through the existing Engine
- Bundle wireguard-go in the agent image
- Fix VXLAN device reuse during repeated reconciliation
- Add lifecycle and compatibility tests and document validation results
@codecov

codecov Bot commented Sep 23, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 91.02750% with 62 lines in your changes missing coverage. Please review.
✅ Project coverage is 46.42%. Comparing base (6fbb6d1) to head (17f1d19).
⚠️ Report is 9 commits behind head on main.

Files with missing lines Patch % Lines
...networkengine/vpndriver/wireguard/process_linux.go 83.80% 12 Missing and 5 partials ⚠️
pkg/networkengine/vpndriver/wireguard/wireguard.go 89.92% 9 Missing and 4 partials ⚠️
.../networkengine/vpndriver/wireguard/device_linux.go 95.40% 6 Missing and 6 partials ⚠️
pkg/engine/tunnel.go 73.17% 11 Missing ⚠️
pkg/engine/engine.go 79.16% 5 Missing ⚠️
...etworkengine/vpndriver/wireguard/recovery_linux.go 96.42% 2 Missing and 2 partials ⚠️
Additional details and impacted files
@@             Coverage Diff             @@
##             main     #199       +/-   ##
===========================================
+ Coverage   28.36%   46.42%   +18.06%     
===========================================
  Files          35       39        +4     
  Lines        3624     4213      +589     
===========================================
+ Hits         1028     1956      +928     
+ Misses       2431     2040      -391     
- Partials      165      217       +52     
Flag Coverage Δ
unittests 46.42% <91.02%> (+18.06%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@njucjc

njucjc commented Sep 24, 2026

Copy link
Copy Markdown
Member

@Jonny-ee Please fix linter, thanks

- Handle cleanup errors, tidy dependencies, and fix Markdown lint
- Isolate route and ipset operations for failure injection
- Add reconciliation, lifecycle, peer update, and readiness tests
@Jonny-ee

Copy link
Copy Markdown
Author

@njucjc Could you please take another look? Thanks!

@njucjc

njucjc commented Oct 9, 2026 •

Copy link
Copy Markdown
Member

@njucjc Could you please take another look? Thanks!

It's a very big PR, could you add a e2e test report?

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 Changes recommended

Userspace close failures can permanently block recovery, and the implementation does not satisfy the linked issue’s central requirements.

2 open findings
What changed in this PR

Adds kernel-first WireGuard userspace fallback with managed recovery and cleanup.

Changes:

  • Bundles and manages wireguard-go.
  • Integrates recovery with Engine reconciliation.
  • Improves VXLAN reuse and adds extensive tests.
File Description
Dockerfile Builds and bundles wireguard-go.
go.mod Reclassifies the errors dependency.
docs/​20260918-support-wireguard-userspace.md Documents fallback design and validation.
hack/​wireguard-fallback-cloud-init.yaml Creates a fallback test environment.
pkg/​networkengine/​vpndriver/​driver.go Adds optional lifecycle injection.
pkg/​networkengine/​vpndriver/​wireguard/​wireguard.go Integrates userspace lifecycle and reconciliation.
pkg/​networkengine/​vpndriver/​wireguard/​device_linux.go Manages backend selection and UAPI access.
pkg/​networkengine/​vpndriver/​wireguard/​device_linux_test.go Tests device management.
pkg/​networkengine/​vpndriver/​wireguard/​process_linux.go Manages the userspace subprocess.
pkg/​networkengine/​vpndriver/​wireguard/​process_linux_test.go Tests subprocess behavior.
pkg/​networkengine/​vpndriver/​wireguard/​recovery_linux.go Implements recovery and backoff.
pkg/​networkengine/​vpndriver/​wireguard/​recovery_linux_test.go Tests recovery policy.
pkg/​networkengine/​vpndriver/​wireguard/​reconcile_linux_test.go Tests reconciliation and cleanup.
pkg/​networkengine/​vpndriver/​wireguard/​regression_linux_test.go Covers peer and cleanup regressions.
pkg/​networkengine/​vpndriver/​wireguard/​regression_integration_linux_test.go Adds privileged regression tests.
pkg/​networkengine/​vpndriver/​wireguard/​integration_linux_test.go Tests fallback networking end-to-end.
pkg/​networkengine/​routedriver/​vxlan/​utils.go Reuses unchanged VXLAN links.
pkg/​networkengine/​routedriver/​vxlan/​utils_test.go Verifies VXLAN reuse.
pkg/​engine/​engine.go Serializes runtime recovery and shutdown.
pkg/​engine/​tunnel.go Integrates lifecycle, health, and cleanup.
pkg/​engine/​runtime.go Schedules driver reconciliation.
pkg/​engine/​recovery_test.go Tests Engine recovery behavior.
pkg/​engine/​compatibility_test.go Tests compatibility with existing drivers.

🧠 Review effort: Balanced


💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +198 to +201
case <-done:
c.mu.Lock()
defer c.mu.Unlock()
return c.closeErr

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

done

| [VXLAN helper](../pkg/networkengine/routedriver/vxlan/utils.go) | Return the existing VXLAN device when its configuration is unchanged. Repeated `LinkAdd` calls previously returned `file exists`, blocking full network recovery after a userspace failure. Only this branch is fixed, with a stronger regression test. |
| Related Go tests and [automatic-fallback environment configuration](../hack/wireguard-fallback-cloud-init.yaml) | Verify backend selection, lifecycle management, shared Engine compatibility, and recovery with real networking. Forced backend selection remains a test-only facility. |

This change does not redesign Raven's dataplane or remove dependencies on VXLAN, TUN, routing, or netfilter. It adds no public backend-selection flag, Gateway field, Node label, CRD, or WireGuard metric. It does not modify Libreswan or the shared ipset implementation, extend VXLAN functionality, or address unrelated historical issues.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Thanks for pointing this out. I agree that the current PR does not implement per-Gateway vpnDriver selection or a reduced-kernel dataplane that removes VXLAN, iptables, and ipset dependencies.

Those were not intended requirements for this change. The intended scope is transparent kernel-to-userspace WireGuard fallback: users select the WireGuard driver, and Raven automatically uses wireguard-go only when the kernel backend is unsupported.

The wording of issue #193 was too broad and conflated these two goals. I will update the issue and PR description to clarify the scope. Reduced-kernel dataplane support, if still needed, should be tracked separately.

@Jonny-ee

Copy link
Copy Markdown
Author

E2E Test Report

Date: 2026-10-11

Commit: 76519ec3608f92ca9613d8c29e42c5edc9117afd (feat/wireguard-go)

Scope and evidence

This report covers the repository's WireGuard network-namespace integration
suites for backend selection, automatic userspace fallback, process recovery,
NAT endpoint preservation and cleanup, plus repository tests and static checks.
These are local runs. The command, environment, duration, exit code and
representative output for each check are recorded in this report. The results
are local execution records, not a CI attestation or hosted CI run.

Test environment

All Go tests and integration suites in this report run directly as root in
the same Multipass VM, without a Docker container. Kernel WireGuard uses
this VM's Linux kernel. Two boot configurations on that same kernel exercise
kernel-supported and kernel-unavailable behavior.

Property Value
Host macOS 26.3.1 arm64; Multipass 1.16.3
VM raven-e2e-20261011; 4 vCPU; 8 GiB RAM
Guest Ubuntu 24.04.5 LTS arm64; Linux 6.8.0-142-generic
Repository Host checkout mounted at /workspace
Go test toolchain Go 1.24.7 linux/arm64
Networking Network namespaces, bridge/veth, iptables 1.8.10 (nf_tables), ipset 7.19 with hash:net,net
Standard boot Kernel WireGuard available; device, regression and network suites
Fallback boot module_blacklist=wireguard; device/network automatic fallback suites
Userspace backend /usr/local/bin/wireguard-go, extracted from the release image built from this commit

The userspace binary used by the integration runs has SHA-256
f7cbec4116d533f9d128780ba794afda5887231a0b7059cbda235282bedc2bb6.

Docker is used only for the release image build and artifact extraction.
It runs on macOS through Docker Desktop 29.6.1's Linux VM
(6.12.76-linuxkit, aarch64), not directly on the macOS kernel. The integration
results below use the Multipass kernel, not the Docker Desktop kernel.

Release image build command (run on the host from the repository root):

docker build --platform linux/arm64 \
  --build-arg TARGETOS=linux --build-arg TARGETARCH=arm64 \
  --build-arg GITCOMMIT=76519ec3608f92ca9613d8c29e42c5edc9117afd \
  -t raven-agent:e2e-report .

VM preparation and execution

The test VM provides Go, iproute2, iptables, ipset, iputils-ping,
util-linux, and the userspace binary from the release image. Before each
boot configuration's test execution:

sudo mkdir -p /var/run/wireguard
sudo sysctl -w net.ipv4.ip_forward=1 \
  net.ipv4.conf.all.rp_filter=0 net.ipv4.conf.default.rp_filter=0
sudo ipset create ravencheck hash:net,net
sudo ipset destroy ravencheck

The directory is the mount point used by isolated per-node UAPI namespaces.
The sysctls enable forwarding and set reverse-path filtering defaults for new
network interfaces. The ipset probe verifies the required set type.

The suites ran sequentially because they reuse namespace and device names. On
the standard boot, the VM preparation and integration commands were:

cd /workspace
sudo -E env PATH="$PATH" RAVEN_WG_DEVICE_INTEGRATION=1 \
  go test ./pkg/networkengine/vpndriver/wireguard \
  -run '^TestWireGuardDeviceIntegration$' -count=1 -v -timeout=15m
sudo -E env PATH="$PATH" RAVEN_WG_REGRESSION_INTEGRATION=1 \
  go test ./pkg/networkengine/vpndriver/wireguard \
  -run '^TestWireGuardRegressionIntegration$' -count=1 -v -timeout=15m
sudo -E env PATH="$PATH" RAVEN_WG_INTEGRATION=1 \
  go test ./pkg/networkengine/vpndriver/wireguard \
  -run '^TestWireGuardNetworkIntegration$' -count=1 -v -timeout=15m

For automatic fallback, module_blacklist=wireguard was added to the VM's
GRUB kernel arguments, sudo update-grub was run, and the VM was rebooted.
The fallback commands were:

cd /workspace
sudo -E env PATH="$PATH" RAVEN_WG_AUTO_FALLBACK_INTEGRATION=device \
  go test ./pkg/networkengine/vpndriver/wireguard \
  -run '^TestWireGuardAutomaticFallbackIntegration$' -count=1 -v -timeout=15m
sudo -E env PATH="$PATH" RAVEN_WG_AUTO_FALLBACK_INTEGRATION=network \
  go test ./pkg/networkengine/vpndriver/wireguard \
  -run '^TestWireGuardAutomaticFallbackIntegration$' -count=1 -v -timeout=15m

The fallback test itself calls real netlink LinkAdd, checks that the error
is EOPNOTSUPP rather than a permission error, and verifies userspace backend
selection. No unsupported-kernel error is mocked. The actual probe output is
included in the recorded-output section below.

Results

Check Result Duration Exit code Recorded output
Release image build (linux/arm64) PASS 29 s 0 Docker Desktop 29.6.1; server kernel 6.12.76-linuxkit; image digest sha256:21170af3838ed0053fe123ee2fd81470da0fd1888c67875303ad8ea9e34c932f
TestWireGuardDeviceIntegration PASS 30.03 s 0 kernel-kernel 0.62 s; kernel-userspace 2.35 s; userspace-userspace 24.13 s; central-relay 2.06 s
TestWireGuardRegressionIntegration PASS 0.93 s 0 learned-NAT-endpoint 0.13 s; UAPI-configuration-rejection 0.10 s; restart-kernel-cleanup 0.05 s
TestWireGuardNetworkIntegration PASS 38.31 s 0 kernel-kernel 2.74 s; kernel-userspace 4.36 s; userspace-userspace 26.24 s; central-relay 4.36 s
TestWireGuardAutomaticFallbackIntegration, device mode PASS 37.59 s 0 direct 34.57 s; central-relay 2.42 s; real probe returned operation not supported
TestWireGuardAutomaticFallbackIntegration, network mode PASS 30.62 s 0 direct 20.70 s; central-relay 9.33 s; real probe returned operation not supported
go test ./... -count=1 -v -timeout=15m PASS 65.78 s 0 Every package returned PASS or [no test files]
go test -race ./pkg/engine/... ./pkg/networkengine/vpndriver/wireguard/... ./pkg/networkengine/routedriver/vxlan/... -count=1 -v -timeout=15m PASS 103.16 s 0 Engine, WireGuard and VXLAN race tests returned PASS
go vet ./... PASS 12.00 s 0 Command returned exit code 0

All listed integration suites explicitly enable their opt-in environment
variable and show their scenario subtests passing. The general repository and
race runs leave opt-in integration/helper tests disabled. Those general runs do not substitute for the
separately executed integration suites.

Recorded output excerpts

The following lines are extracted from the local run output. Durations in
--- PASS lines measure test execution; the Results table records total
command wall time, including Go startup/build time. Complete logs are not
included with this report.

# Device: 2026-10-11 08:26:39–08:27:09 UTC
--- PASS: TestWireGuardDeviceIntegration (29.17s)
    --- PASS: TestWireGuardDeviceIntegration/kernel-kernel (0.62s)
    --- PASS: TestWireGuardDeviceIntegration/kernel-userspace (2.35s)
    --- PASS: TestWireGuardDeviceIntegration/userspace-userspace (24.13s)
    --- PASS: TestWireGuardDeviceIntegration/central-relay (2.06s)
WALL_SECONDS=30.03
EXIT_CODE=0

# Regression: 2026-10-11 08:27:09–08:27:10 UTC
--- PASS: TestWireGuardRegressionIntegration (0.28s)
    --- PASS: TestWireGuardRegressionIntegration/learned-NAT-endpoint (0.13s)
    --- PASS: TestWireGuardRegressionIntegration/UAPI-configuration-rejection (0.10s)
    --- PASS: TestWireGuardRegressionIntegration/restart-kernel-cleanup (0.05s)
WALL_SECONDS=0.93
EXIT_CODE=0

# Network: 2026-10-11 08:27:10–08:27:48 UTC
--- PASS: TestWireGuardNetworkIntegration (37.71s)
    --- PASS: TestWireGuardNetworkIntegration/kernel-kernel (2.74s)
    --- PASS: TestWireGuardNetworkIntegration/kernel-userspace (4.36s)
    --- PASS: TestWireGuardNetworkIntegration/userspace-userspace (26.24s)
    --- PASS: TestWireGuardNetworkIntegration/central-relay (4.36s)
WALL_SECONDS=38.31
EXIT_CODE=0

# Fallback device: 2026-10-11 08:32:44–08:33:21 UTC
    integration_linux_test.go:322: real kernel WireGuard creation returned operation not supported
--- PASS: TestWireGuardAutomaticFallbackIntegration (36.99s)
    --- PASS: TestWireGuardAutomaticFallbackIntegration/direct (34.57s)
    --- PASS: TestWireGuardAutomaticFallbackIntegration/central-relay (2.42s)
WALL_SECONDS=37.59
EXIT_CODE=0

# Fallback network: 2026-10-11 08:33:21–08:33:52 UTC
    integration_linux_test.go:322: real kernel WireGuard creation returned operation not supported
--- PASS: TestWireGuardAutomaticFallbackIntegration (30.03s)
    --- PASS: TestWireGuardAutomaticFallbackIntegration/direct (20.70s)
    --- PASS: TestWireGuardAutomaticFallbackIntegration/central-relay (9.33s)
WALL_SECONDS=30.62
EXIT_CODE=0

Coverage-to-test mapping

Each behavior below maps to a passing test or subtest. Durations and results
are recorded above.

Behavior under test Exact test / subtest
Kernel WireGuard device creation, peer setup and cleanup TestWireGuardDeviceIntegration/kernel-kernel
Kernel-to-userspace interoperability and userspace process recovery TestWireGuardDeviceIntegration/kernel-userspace
Two userspace peers and process kill/replacement recovery TestWireGuardDeviceIntegration/userspace-userspace
Central relay with mixed backend peers TestWireGuardDeviceIntegration/central-relay
Network namespace setup, cross-pool Pod ping and UDP source-address check using kernel devices TestWireGuardNetworkIntegration/kernel-kernel
The same network checks with one userspace backend TestWireGuardNetworkIntegration/kernel-userspace
The same network checks with two userspace backends, including recovery TestWireGuardNetworkIntegration/userspace-userspace
Network-mode central relay, route/NAT path and reverse connectivity TestWireGuardNetworkIntegration/central-relay
Real unsupported-kernel probe and automatic userspace selection in device mode TestWireGuardAutomaticFallbackIntegration/direct, .../central-relay with RAVEN_WG_AUTO_FALLBACK_INTEGRATION=device
Real unsupported-kernel probe and automatic userspace selection in network mode TestWireGuardAutomaticFallbackIntegration/direct, .../central-relay with RAVEN_WG_AUTO_FALLBACK_INTEGRATION=network
Learned NAT endpoint preservation TestWireGuardRegressionIntegration/learned-NAT-endpoint
Real UAPI configuration rejection while retaining a healthy process TestWireGuardRegressionIntegration/UAPI-configuration-rejection
Cleanup of an existing kernel device during restart TestWireGuardRegressionIntegration/restart-kernel-cleanup
Package-wide regression coverage go test ./... -count=1 -v -timeout=15m
Race detector coverage for engine, WireGuard and VXLAN packages go test -race ./pkg/engine/... ./pkg/networkengine/vpndriver/wireguard/... ./pkg/networkengine/routedriver/vxlan/... -count=1 -v -timeout=15m

The integration test source is integration_linux_test.go and the regression source is regression_integration_linux_test.go. The general repository and race commands do not enable the opt-in integration tests; those are covered by the explicit rows above.

Known limitations, untested areas and risks

  • Runtime evidence covers one architecture (arm64), one distribution
    (Ubuntu 24.04.5) and one kernel line (6.8.0-142-generic). There is no
    amd64, other-distribution or kernel-version matrix.
  • The fallback condition is produced by blacklisting WireGuard on this Linux
    6.8 kernel. This proves the real EOPNOTSUPP path, but does not prove
    behavior on pre-5.6 kernels or on kernels with different netlink, ipset or
    netfilter implementations.
  • The topology is an isolated network-namespace test on one VM. It does not
    test a deployed Kubernetes multi-node cluster, cloud routing, real external
    NAT devices, cross-host MTU variation or a production CNI datapath.
  • No long-running soak, throughput/latency benchmark, upgrade/rollback,
    power-loss recovery, IPv6 matrix, or security/privilege-hardening test is
    included.
  • The release image build verifies the linux/arm64 image and the extracted
    wireguard-go artifact. It does not verify a full Alpine or Kubernetes
    deployment lifecycle.
  • There is no hosted CI result or external log service link; the evidence is
    local output excerpts recorded in this report; full logs are not attached. The VM tests require
    root privileges, network namespaces, bridge/veth, iptables and
    hash:net,net ipset support.

These boundaries leave residual risk for unsupported architectures, kernel and
distribution combinations, production networking, and operational behavior
outside the isolated test lifecycle.

Additional scenarios not covered by this E2E run

Scenario Status in this report Existing narrower coverage
Two or three independent physical/VM nodes with real cross-host NAT, MTU variation, packet loss and network partition Not tested; all integration nodes are network namespaces inside one VM The network suites validate cross-pool routing, ping and UDP source address inside that VM.
Missing or non-executable wireguard-go, unavailable TUN device, occupied or stale UAPI socket, and cleanup failures Partially covered by unit tests, not as a multi-node E2E fault campaign TestUserspaceStartupFailures, TestProcessRefusesOccupiedSocket, TestProcessRefusesRegularUAPIFile, TestProcessSocketCleanupFailureCanBeRetried, TestCleanupContinuesAfterTUNDisappears and TestDeviceCleanupProtectsForeignLinkAndRetriesDelete.
Disk/fd exhaustion, node reboot, network partition recovery and repeated crash-loop backoff Not tested as E2E scenarios TestUserspaceStartBackoffSurvivesConfigurationEvents, TestUserspaceExitQueuesBeforeWorkerRecovery and TestProcessUnexpectedExitAndRestart cover bounded simulated failures.
Runtime kernel-to-userspace switch, or userspace-to-kernel switch after startup Not tested Backend selection is tested at initialization; fallback is tested when the initial kernel probe returns EOPNOTSUPP.
Key rotation, peer add/remove and full runtime AllowedIPs lifecycle Partially covered TestChangedPeerConfiguration, TestChangedPeerFieldsAreApplied, TestPeerUpdatesKeepKernelBehavior and regression UAPI-configuration-rejection cover configuration changes; no end-to-end key rotation campaign was run.
Raven engine driven by real Kubernetes Gateway/Node CRDs across a deployed cluster Not tested The repository tests use Go fixtures and the integration suites use synthetic endpoint data.
Upgrade/rollback from an older Raven version with pre-existing kernel, iptables or ipset state Not tested Current tests verify cleanup and restart behavior within this commit only.
Throughput/latency comparison, long-running soak, resource leak and repeated process churn Not tested Race tests and process recovery assertions are bounded correctness checks, not performance or soak tests.
VXLAN repeated reconcile without traffic interruption Not tested as E2E TestVxlan_Apply, Test_applyRoutes and Test_applyRule are package-level tests; no deployed VXLAN traffic test was run.

Conclusion and release recommendation

All listed WireGuard integration, fallback, regression, repository, race and
static-check commands passed with exit code 0 on commit
76519ec3608f92ca9613d8c29e42c5edc9117afd. The evidence supports merging and
releasing this change for the validated target of arm64 Ubuntu 24.04 with a
Linux 6.8 kernel
, subject to the repository's normal review and CI gates.

This report should not be used as a compatibility sign-off for amd64, other
distributions, older kernels, deployed Kubernetes environments or the
untested operational cases above. If those environments are release targets,
add a runtime matrix for them before making a broader compatibility claim.

@Jonny-ee

Copy link
Copy Markdown
Author

@njucjc Could you please take another look? Thanks!

It's a very big PR, could you add a e2e test report?

done

@zyjhtangtang
zyjhtangtang merged commit 380dc93 into openyurtio:main Oct 12, 2026
11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Support Gateway-Level VPN Driver Selection for Reduced-Kernel NodePools

4 participants