[PR #7024] client/iface: let a TunAdapter supply the tun.Device itself #29456

Open
opened 2026-08-05 08:08:06 -04:00 by saavagebueno · 0 comments
Owner

Original Pull Request: https://github.com/netbirdio/netbird/pull/7024

State: open
Merged: No


Implements the change discussed in #7023. Opening it so the shape is concrete — happy to rework the API if you would prefer a different one.

Problem

Android permits exactly one active VpnService per user profile. An application running NetBird alongside another overlay backend must therefore own that tun itself and route each packet to whichever backend claims its destination — it has no second tun to hand over, and handing over the shared one would give NetBird every other backend's traffic.

The only Android injection point is TunAdapter.ConfigureInterface, which returns a file descriptor that goes to tun.CreateUnmonitoredTUNFromFD. That ioctls TUNGETIFF, so it accepts only a real tun.

Change

TunDeviceProvider, an optional interface a TunAdapter may also implement:

type TunDeviceProvider interface {
    TunDevice(address, addressV6 string, mtu int, dns, searchDomains, routes string) (tun.Device, string, error)
}

WGTunDevice.Create type-asserts for it. When present it asks for the device; when absent the existing descriptor path runs unchanged, so every current caller is unaffected. gomobile cannot express a Go interface return, so gomobile bindings cannot accidentally implement it either.

49 lines across adapter.go and device_android.go.

Verification

Builds for android/arm64. Running in an application that carries ZeroTier, WireGuard, Tailscale and NetBird simultaneously on one tun — NetBird in its own process, reading and writing a channel-backed tun.Device — verified on a Galaxy S23 (Android 16): all four providers reachable, and NetBird still reachable after 90 minutes in deep Doze.

No test included: exercising it needs a tun.Device implementation and an Android host, and I did not want to add a mock without knowing whether you would want one and in what shape. Glad to add whatever you prefer.

Summary by CodeRabbit

  • New Features

    • Added support for compatible adapters to provide a TUN device and interface name directly.
    • Existing descriptor-based adapter behavior remains supported.
  • Bug Fixes

    • Improved error handling during TUN device creation.
    • Ensured resources are properly closed when device creation fails.
**Original Pull Request:** https://github.com/netbirdio/netbird/pull/7024 **State:** open **Merged:** No --- Implements the change discussed in #7023. Opening it so the shape is concrete — happy to rework the API if you would prefer a different one. ## Problem Android permits exactly one active `VpnService` per user profile. An application running NetBird alongside another overlay backend must therefore own that tun itself and route each packet to whichever backend claims its destination — it has no second tun to hand over, and handing over the shared one would give NetBird every other backend's traffic. The only Android injection point is `TunAdapter.ConfigureInterface`, which returns a file descriptor that goes to `tun.CreateUnmonitoredTUNFromFD`. That ioctls `TUNGETIFF`, so it accepts only a real tun. ## Change `TunDeviceProvider`, an optional interface a `TunAdapter` may also implement: ```go type TunDeviceProvider interface { TunDevice(address, addressV6 string, mtu int, dns, searchDomains, routes string) (tun.Device, string, error) } ``` `WGTunDevice.Create` type-asserts for it. When present it asks for the device; when absent the existing descriptor path runs unchanged, so every current caller is unaffected. gomobile cannot express a Go interface return, so gomobile bindings cannot accidentally implement it either. 49 lines across `adapter.go` and `device_android.go`. ## Verification Builds for `android/arm64`. Running in an application that carries ZeroTier, WireGuard, Tailscale and NetBird simultaneously on one tun — NetBird in its own process, reading and writing a channel-backed `tun.Device` — verified on a Galaxy S23 (Android 16): all four providers reachable, and NetBird still reachable after 90 minutes in deep Doze. No test included: exercising it needs a `tun.Device` implementation and an Android host, and I did not want to add a mock without knowing whether you would want one and in what shape. Glad to add whatever you prefer. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added support for compatible adapters to provide a TUN device and interface name directly. * Existing descriptor-based adapter behavior remains supported. * **Bug Fixes** * Improved error handling during TUN device creation. * Ensured resources are properly closed when device creation fails. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
saavagebueno added the pull-request label 2026-08-05 08:08:06 -04:00
Sign in to join this conversation.
No Label pull-request
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: DYNR/netbird#29456