mirror of
https://github.com/jmcorgan/fips.git
synced 2026-07-22 07:48:26 +00:00
feat: Android-ready core: target_os gating and app-owned TUN seam
Make the FIPS core build and run as an embedded Android library. The host app owns the TUN (e.g. an Android VpnService) and FIPS performs no system-TUN or CAP_NET_ADMIN operations. Squashed from the following changes: - gate desktop transports/TUN by target_os, not features: a plain `cargo build` now compiles for every target with no flags. Ethernet (raw AF_PACKET / BPF) is gated to linux/macos, so Android (target_os = "android", not "linux") self-excludes it as Windows already did; real system-TUN ops are gated per linux/macos and Android gets a no-op stub; the ipi6_ifindex cast handles it being i32 on Android vs u32 on macOS. No Cargo features are introduced; desktop builds are unchanged. - app-owned TUN seam: Node::enable_app_owned_tun() lets an embedder that owns the TUN fd exchange IPv6 packet bytes with FIPS over channels instead of FIPS creating a system TUN device. It returns (app_outbound_tx, app_inbound_rx): the embedder pushes packets read from its fd into the outbound sender (app -> mesh) and pulls packets destined for its fd from the inbound receiver (mesh -> app). start() gates system-TUN creation on tun_tx being unset, so with the channels pre-installed it skips device creation and does no system-TUN ops; both directions reuse the existing inbound-shim and run_rx_loop wiring. Packets entering via app_outbound_tx bypass handle_tun_packet, so the embedder must push only fd00::/8-destined packets and clamp TCP MSS on outbound SYNs; the rustdoc and the IPv6-adapter design doc spell this out. - keep the android target warning-clean so the cross-compile check passes clippy -D warnings. - add an Android cross-compile CI check: cross-compile the library for aarch64-linux-android via cargo-ndk and run clippy -D warnings. Android ships as an embedded library (the host app owns the TUN), so there is no daemon binary to package; this is a check job, not a packaging one. - docs: list Android as a supported platform. Tests: app_owned_tun_seam_wires_channels covers the channel round-trip and the Active state; start_skips_system_tun_when_app_owned runs start() and asserts no named system device is created.
This commit is contained in:
42
.github/workflows/ci.yml
vendored
42
.github/workflows/ci.yml
vendored
@@ -88,6 +88,48 @@ jobs:
|
||||
${{ runner.os }}-cargo-
|
||||
- run: cargo clippy --all-targets --all-features -- -D warnings
|
||||
|
||||
# ───────────────────────────────────────────────────────────────────────────
|
||||
# Android cross-check
|
||||
#
|
||||
# FIPS runs on Android as an embedded library — the host app owns the TUN
|
||||
# (an Android VpnService), so there are no daemon binaries to package, unlike
|
||||
# the desktop targets. This job only cross-compiles the library for the
|
||||
# android target to guard the android-only cfg paths (and the `not(android)`
|
||||
# exclusions) from silently bit-rotting; nothing else in CI compiles them.
|
||||
# cargo-ndk wires the NDK toolchain, which is required even for a check
|
||||
# because `ring` compiles C at build time.
|
||||
# ───────────────────────────────────────────────────────────────────────────
|
||||
android-check:
|
||||
name: Android cross-check (aarch64)
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- name: Install Rust toolchain (+ Android target)
|
||||
uses: actions-rust-lang/setup-rust-toolchain@v1
|
||||
with:
|
||||
target: aarch64-linux-android
|
||||
components: clippy
|
||||
cache: false
|
||||
rustflags: ''
|
||||
- name: Cache Cargo registry + build
|
||||
uses: actions/cache@v5
|
||||
with:
|
||||
path: |
|
||||
~/.cargo/registry
|
||||
~/.cargo/git
|
||||
target
|
||||
key: ${{ runner.os }}-cargo-android-${{ hashFiles('**/Cargo.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-cargo-
|
||||
- name: Install cargo-ndk
|
||||
uses: taiki-e/install-action@v2
|
||||
with:
|
||||
tool: cargo-ndk
|
||||
- name: Clippy the library for Android
|
||||
run: |
|
||||
export ANDROID_NDK_HOME="${ANDROID_NDK_HOME:-$ANDROID_NDK_LATEST_HOME}"
|
||||
cargo ndk -t arm64-v8a clippy --lib -- -D warnings
|
||||
|
||||
build:
|
||||
name: Build (${{ matrix.os }})
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
22
README.md
22
README.md
@@ -112,17 +112,19 @@ tutorial progression starting at
|
||||
cargo build --release
|
||||
```
|
||||
|
||||
Requires Rust 1.94.1+ (edition 2024). Linux, macOS, and Windows are
|
||||
supported; transport availability varies by platform.
|
||||
Requires Rust 1.94.1+ (edition 2024). Linux, macOS, and Windows run as
|
||||
standalone daemons; Android is supported as an embedded library (the host
|
||||
app owns the TUN, e.g. a `VpnService`). Transport availability varies by
|
||||
platform.
|
||||
|
||||
| Transport | Linux | macOS | Windows | OpenWrt |
|
||||
|-----------|:-----:|:-----:|:-------:|:-------:|
|
||||
| UDP | ✅ | ✅ | ✅ | ✅ |
|
||||
| TCP | ✅ | ✅ | ✅ | ✅ |
|
||||
| Ethernet | ✅ | ✅ | ❌ | ✅ |
|
||||
| Tor | ✅ | ✅ | ✅ | ✅ |
|
||||
| Nym | ✅ | ✅ | ✅ | ❌ |
|
||||
| BLE | ✅ | ❌ | ❌ | ❌ |
|
||||
| Transport | Linux | macOS | Windows | Android | OpenWrt |
|
||||
|-----------|:-----:|:-----:|:-------:|:-------:|:-------:|
|
||||
| UDP | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||
| TCP | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||
| Ethernet | ✅ | ✅ | ❌ | ❌ | ✅ |
|
||||
| Tor | ✅ | ✅ | ✅ | ❌ | ✅ |
|
||||
| Nym | ✅ | ✅ | ✅ | ❌ | ❌ |
|
||||
| BLE | ✅ | ❌ | ❌ | ❌ | ❌ |
|
||||
|
||||
On Linux, a source build requires `libclang` — the LAN gateway's
|
||||
nftables bindings are generated by `bindgen` at build time, which
|
||||
|
||||
@@ -302,6 +302,34 @@ alternative — running under a dedicated unprivileged service
|
||||
account with the capability granted on the binary — see
|
||||
[../how-to/run-as-unprivileged-user.md](../how-to/run-as-unprivileged-user.md).
|
||||
|
||||
### App-Owned TUN (embedded hosts)
|
||||
|
||||
On platforms where FIPS is embedded rather than run as a daemon — notably
|
||||
Android, where the `VpnService` owns the TUN fd and the app has no
|
||||
`CAP_NET_ADMIN` — FIPS does not create `fips0` itself. Instead the embedder owns
|
||||
the fd and exchanges IPv6 packet bytes with FIPS over channels.
|
||||
|
||||
`Node::enable_app_owned_tun()` sets this up. It is called after `Node::new` and
|
||||
before `start()` (and before the node is moved into a background task), mirroring
|
||||
`control_read_handle()`, and returns two app-side channel ends:
|
||||
|
||||
- **app → mesh** — the embedder pushes IPv6 packets read from its fd into
|
||||
`app_outbound_tx`. These are drained by `run_rx_loop` into `handle_tun_outbound`
|
||||
and routed exactly as the Reader Thread's output would be.
|
||||
- **mesh → app** — inbound mesh traffic on port 256 is reconstructed and written
|
||||
to the node's `tun_tx` (the same sink the Writer Thread reads); the embedder
|
||||
pulls from `app_inbound_rx` and writes to its fd.
|
||||
|
||||
With the channels installed, `start()` skips system-TUN creation (it gates on
|
||||
`tun_tx` being unset), so FIPS does no `CAP_NET_ADMIN` operations.
|
||||
|
||||
Because packets enter via `app_outbound_tx` rather than the Reader Thread, they
|
||||
**bypass `handle_tun_packet`** — the `fd00::/8` destination filter, the ICMPv6
|
||||
Destination Unreachable for off-mesh dests (see [Reader Thread](#reader-thread)),
|
||||
and the [TUN-Side TCP MSS Clamping](#tun-side-tcp-mss-clamping). The embedder is
|
||||
therefore responsible for routing only `fd00::/8` to its TUN (so only mesh-bound
|
||||
packets arrive) and for clamping TCP MSS on outbound SYNs.
|
||||
|
||||
## Implementation Status
|
||||
|
||||
| Feature | Status |
|
||||
|
||||
@@ -45,12 +45,10 @@ impl Node {
|
||||
/// (e.g. only non-UDP transports). Enabled on Linux and macOS:
|
||||
/// both kernels route a matching peer 5-tuple to the connected
|
||||
/// socket when it shares the wildcard listen port via SO_REUSEPORT.
|
||||
/// Only compiled on Linux/macOS — the sole caller (the rx_loop tick) is
|
||||
/// gated the same way, so on other targets (android) there is nothing to do.
|
||||
#[cfg(any(target_os = "linux", target_os = "macos"))]
|
||||
pub(in crate::node) async fn activate_connected_udp_sessions(&mut self) {
|
||||
#[cfg(not(any(target_os = "linux", target_os = "macos")))]
|
||||
{
|
||||
// No-op on platforms without the connected-UDP fast path.
|
||||
}
|
||||
#[cfg(any(target_os = "linux", target_os = "macos"))]
|
||||
{
|
||||
if !connected_udp_enabled() {
|
||||
return;
|
||||
|
||||
@@ -1312,7 +1312,10 @@ impl Node {
|
||||
// Singleton child booleans, with today's exact enable conditions.
|
||||
let nostr = self.config().node.rendezvous.nostr.enabled;
|
||||
let mdns = self.config().node.rendezvous.lan.enabled;
|
||||
let tun = self.config().tun.enabled;
|
||||
// No Tun child when the TUN is app-owned (the embedder pre-set
|
||||
// `tun_tx` via `enable_app_owned_tun`) — FIPS does no system-TUN ops;
|
||||
// the channels installed before `start` carry both directions.
|
||||
let tun = self.config().tun.enabled && self.supervisor.tun_tx.is_none();
|
||||
let dns = self.config().dns.enabled;
|
||||
|
||||
// Worker-pool booleans + counts. Unix only — the workers issue
|
||||
|
||||
@@ -50,7 +50,7 @@ use crate::proto::lookup::{Lookup, LookupBackoff, LookupForwardRateLimiter};
|
||||
use crate::proto::mmp::Mmp;
|
||||
use crate::proto::routing::{self, Router, RoutingErrorRateLimiter};
|
||||
use crate::proto::stp::TreeState;
|
||||
#[cfg(unix)]
|
||||
#[cfg(any(target_os = "linux", target_os = "macos"))]
|
||||
use crate::transport::ethernet::EthernetTransport;
|
||||
use crate::transport::nym::NymTransport;
|
||||
use crate::transport::tcp::TcpTransport;
|
||||
@@ -62,7 +62,7 @@ use crate::transport::{
|
||||
};
|
||||
use crate::upper::hosts::HostMap;
|
||||
use crate::upper::icmp_rate_limit::IcmpRateLimiter;
|
||||
use crate::upper::tun::{TunError, TunState, TunTx};
|
||||
use crate::upper::tun::{TunError, TunOutboundTx, TunState, TunTx};
|
||||
use crate::utils::index::IndexAllocator;
|
||||
use crate::{Config, ConfigError, Identity, IdentityError, NodeAddr, PeerIdentity, TreeCoordinate};
|
||||
use rand::Rng;
|
||||
@@ -873,7 +873,7 @@ impl Node {
|
||||
}
|
||||
|
||||
// Create Ethernet transport instances (Unix only — requires raw sockets)
|
||||
#[cfg(unix)]
|
||||
#[cfg(any(target_os = "linux", target_os = "macos"))]
|
||||
{
|
||||
let eth_instances: Vec<_> = self
|
||||
.config()
|
||||
@@ -1007,7 +1007,7 @@ impl Node {
|
||||
&self,
|
||||
addr_str: &str,
|
||||
) -> Result<(TransportId, TransportAddr), NodeError> {
|
||||
#[cfg(unix)]
|
||||
#[cfg(any(target_os = "linux", target_os = "macos"))]
|
||||
{
|
||||
let (iface, mac_str) = addr_str.split_once('/').ok_or_else(|| {
|
||||
NodeError::NoTransportForType(format!(
|
||||
@@ -1039,7 +1039,7 @@ impl Node {
|
||||
|
||||
Ok((transport_id, TransportAddr::from_bytes(&mac)))
|
||||
}
|
||||
#[cfg(not(unix))]
|
||||
#[cfg(not(any(target_os = "linux", target_os = "macos")))]
|
||||
{
|
||||
Err(NodeError::NoTransportForType(
|
||||
"Ethernet transport is not supported on this platform".to_string(),
|
||||
@@ -2691,6 +2691,36 @@ impl Node {
|
||||
self.supervisor.tun_tx.as_ref()
|
||||
}
|
||||
|
||||
/// Set up an **app-owned TUN**: rather than FIPS creating a system TUN
|
||||
/// device, the embedder (e.g. an Android `VpnService`) owns the fd and
|
||||
/// exchanges IPv6 packet bytes with FIPS over the returned channels. Call
|
||||
/// this after [`Node::new`] and **before** [`Self::start`] — and before
|
||||
/// moving the node into a background task.
|
||||
///
|
||||
/// Returns `(app_outbound_tx, app_inbound_rx)`:
|
||||
/// - push IPv6 packets read from the app's TUN fd into `app_outbound_tx`
|
||||
/// (app → mesh); FIPS routes them to the destination node.
|
||||
/// - pull IPv6 packets destined for the app's TUN fd from `app_inbound_rx`
|
||||
/// (mesh → app) and write them to the fd (`recv_timeout` for clean stop).
|
||||
///
|
||||
/// With this set, [`Self::start`] skips system-TUN creation (it gates on
|
||||
/// `tun_tx` being unset). Packets pushed into `app_outbound_tx` bypass the
|
||||
/// system-TUN reader's `handle_tun_packet`, so the embedder must do what that
|
||||
/// path otherwise would: push only `fd::/8`-destined IPv6 packets — FIPS no
|
||||
/// longer filters the destination or emits ICMPv6 unreachable for off-mesh
|
||||
/// dests — and clamp TCP MSS on outbound SYNs.
|
||||
pub fn enable_app_owned_tun(&mut self) -> (TunOutboundTx, std::sync::mpsc::Receiver<Vec<u8>>) {
|
||||
let tun_channel_size = self.config().node.buffers.tun_channel;
|
||||
// app → mesh: the app pushes; `run_rx_loop` drains `tun_outbound_rx`.
|
||||
let (outbound_tx, outbound_rx) = tokio::sync::mpsc::channel(tun_channel_size);
|
||||
// mesh → app: the node writes inbound packets to `tun_tx`; the app pulls.
|
||||
let (tun_tx, tun_rx) = std::sync::mpsc::channel();
|
||||
self.supervisor.tun_tx = Some(tun_tx);
|
||||
self.supervisor.tun_outbound_rx = Some(outbound_rx);
|
||||
self.tun_state = TunState::Active;
|
||||
(outbound_tx, tun_rx)
|
||||
}
|
||||
|
||||
// === Sending ===
|
||||
|
||||
/// Encrypt and send a link-layer message to an authenticated peer.
|
||||
|
||||
@@ -2137,3 +2137,66 @@ async fn handle_msg1_admits_existing_peer_at_cap() {
|
||||
"rate limiter must rebalance after the (bypass-admitted) handler returns"
|
||||
);
|
||||
}
|
||||
|
||||
/// App-owned TUN seam: `enable_app_owned_tun` wires the embedder's packet
|
||||
/// channels (an Android `VpnService` owns the fd) and marks the TUN active so
|
||||
/// `start()` skips system-TUN creation.
|
||||
#[test]
|
||||
fn app_owned_tun_seam_wires_channels() {
|
||||
let mut config = crate::Config::new();
|
||||
config.tun.enabled = true;
|
||||
let mut node = make_node_with(config);
|
||||
|
||||
let (outbound_tx, tun_rx) = node.enable_app_owned_tun();
|
||||
|
||||
// TUN is active and the inbound (mesh→app) sender is installed, so `start()`
|
||||
// will skip `TunDevice::create` (it gates on `tun_tx.is_none()`).
|
||||
assert_eq!(node.tun_state(), crate::upper::tun::TunState::Active);
|
||||
assert!(node.tun_tx().is_some(), "inbound sender installed");
|
||||
|
||||
// mesh → app: a packet the node delivers to its `tun_tx` reaches the app's rx.
|
||||
let pkt = vec![0x60u8, 0, 0, 0, 0, 0];
|
||||
node.tun_tx().unwrap().send(pkt.clone()).unwrap();
|
||||
assert_eq!(
|
||||
tun_rx
|
||||
.recv_timeout(std::time::Duration::from_millis(200))
|
||||
.unwrap(),
|
||||
pkt,
|
||||
"the app pulls the same bytes the node wrote",
|
||||
);
|
||||
|
||||
// app → mesh: the returned sender is live (its matching rx is held by the node
|
||||
// and drained by `run_rx_loop` → `handle_tun_outbound`).
|
||||
assert!(outbound_tx.try_send(vec![0x60]).is_ok());
|
||||
}
|
||||
|
||||
/// With an app-owned TUN configured, `start()` must NOT create a system TUN
|
||||
/// device: it leaves `tun_name` unset (a real device records its interface name)
|
||||
/// and keeps the TUN `Active` with the app-owned channels.
|
||||
#[tokio::test]
|
||||
async fn start_skips_system_tun_when_app_owned() {
|
||||
// Mirror `make_healthy_node` (one loopback UDP transport so bring-up
|
||||
// reaches `Full`), plus tun.enabled so the Tun child WOULD spawn if the
|
||||
// app-owned gate failed.
|
||||
let mut config = crate::Config::new();
|
||||
config.transports.udp = crate::config::TransportInstances::Single(crate::config::UdpConfig {
|
||||
bind_addr: Some("127.0.0.1:0".to_string()),
|
||||
..Default::default()
|
||||
});
|
||||
config.dns.enabled = false;
|
||||
config.tun.enabled = true;
|
||||
let mut node = make_node_with(config);
|
||||
|
||||
let (_outbound_tx, _tun_rx) = node.enable_app_owned_tun();
|
||||
node.start().await.unwrap();
|
||||
|
||||
// No system device was created (that path records the interface name); the
|
||||
// app-owned TUN stayed active.
|
||||
assert!(
|
||||
node.tun_name().is_none(),
|
||||
"app-owned TUN must not create a named system device",
|
||||
);
|
||||
assert_eq!(node.tun_state(), crate::upper::tun::TunState::Active);
|
||||
|
||||
node.stop().await.unwrap();
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ pub mod tcp;
|
||||
pub mod tor;
|
||||
pub mod udp;
|
||||
|
||||
#[cfg(unix)]
|
||||
#[cfg(any(target_os = "linux", target_os = "macos"))]
|
||||
pub mod ethernet;
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
@@ -20,7 +20,7 @@ pub mod ble;
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
use ble::DefaultBleTransport;
|
||||
#[cfg(unix)]
|
||||
#[cfg(any(target_os = "linux", target_os = "macos"))]
|
||||
use ethernet::EthernetTransport;
|
||||
#[cfg(test)]
|
||||
use loopback::LoopbackTransport;
|
||||
@@ -664,7 +664,7 @@ pub enum TransportHandle {
|
||||
/// UDP/IP transport.
|
||||
Udp(UdpTransport),
|
||||
/// Raw Ethernet transport.
|
||||
#[cfg(unix)]
|
||||
#[cfg(any(target_os = "linux", target_os = "macos"))]
|
||||
Ethernet(EthernetTransport),
|
||||
/// TCP/IP transport.
|
||||
Tcp(TcpTransport),
|
||||
@@ -685,7 +685,7 @@ impl TransportHandle {
|
||||
pub async fn start(&mut self) -> Result<(), TransportError> {
|
||||
match self {
|
||||
TransportHandle::Udp(t) => t.start_async().await,
|
||||
#[cfg(unix)]
|
||||
#[cfg(any(target_os = "linux", target_os = "macos"))]
|
||||
TransportHandle::Ethernet(t) => t.start_async().await,
|
||||
TransportHandle::Tcp(t) => t.start_async().await,
|
||||
TransportHandle::Tor(t) => t.start_async().await,
|
||||
@@ -701,7 +701,7 @@ impl TransportHandle {
|
||||
pub async fn stop(&mut self) -> Result<(), TransportError> {
|
||||
match self {
|
||||
TransportHandle::Udp(t) => t.stop_async().await,
|
||||
#[cfg(unix)]
|
||||
#[cfg(any(target_os = "linux", target_os = "macos"))]
|
||||
TransportHandle::Ethernet(t) => t.stop_async().await,
|
||||
TransportHandle::Tcp(t) => t.stop_async().await,
|
||||
TransportHandle::Tor(t) => t.stop_async().await,
|
||||
@@ -717,7 +717,7 @@ impl TransportHandle {
|
||||
pub async fn send(&self, addr: &TransportAddr, data: &[u8]) -> Result<usize, TransportError> {
|
||||
match self {
|
||||
TransportHandle::Udp(t) => t.send_async(addr, data).await,
|
||||
#[cfg(unix)]
|
||||
#[cfg(any(target_os = "linux", target_os = "macos"))]
|
||||
TransportHandle::Ethernet(t) => t.send_async(addr, data).await,
|
||||
TransportHandle::Tcp(t) => t.send_async(addr, data).await,
|
||||
TransportHandle::Tor(t) => t.send_async(addr, data).await,
|
||||
@@ -733,7 +733,7 @@ impl TransportHandle {
|
||||
pub fn transport_id(&self) -> TransportId {
|
||||
match self {
|
||||
TransportHandle::Udp(t) => t.transport_id(),
|
||||
#[cfg(unix)]
|
||||
#[cfg(any(target_os = "linux", target_os = "macos"))]
|
||||
TransportHandle::Ethernet(t) => t.transport_id(),
|
||||
TransportHandle::Tcp(t) => t.transport_id(),
|
||||
TransportHandle::Tor(t) => t.transport_id(),
|
||||
@@ -749,7 +749,7 @@ impl TransportHandle {
|
||||
pub fn name(&self) -> Option<&str> {
|
||||
match self {
|
||||
TransportHandle::Udp(t) => t.name(),
|
||||
#[cfg(unix)]
|
||||
#[cfg(any(target_os = "linux", target_os = "macos"))]
|
||||
TransportHandle::Ethernet(t) => t.name(),
|
||||
TransportHandle::Tcp(t) => t.name(),
|
||||
TransportHandle::Tor(t) => t.name(),
|
||||
@@ -765,7 +765,7 @@ impl TransportHandle {
|
||||
pub fn transport_type(&self) -> &TransportType {
|
||||
match self {
|
||||
TransportHandle::Udp(t) => t.transport_type(),
|
||||
#[cfg(unix)]
|
||||
#[cfg(any(target_os = "linux", target_os = "macos"))]
|
||||
TransportHandle::Ethernet(t) => t.transport_type(),
|
||||
TransportHandle::Tcp(t) => t.transport_type(),
|
||||
TransportHandle::Tor(t) => t.transport_type(),
|
||||
@@ -781,7 +781,7 @@ impl TransportHandle {
|
||||
pub fn state(&self) -> TransportState {
|
||||
match self {
|
||||
TransportHandle::Udp(t) => t.state(),
|
||||
#[cfg(unix)]
|
||||
#[cfg(any(target_os = "linux", target_os = "macos"))]
|
||||
TransportHandle::Ethernet(t) => t.state(),
|
||||
TransportHandle::Tcp(t) => t.state(),
|
||||
TransportHandle::Tor(t) => t.state(),
|
||||
@@ -797,7 +797,7 @@ impl TransportHandle {
|
||||
pub fn mtu(&self) -> u16 {
|
||||
match self {
|
||||
TransportHandle::Udp(t) => t.mtu(),
|
||||
#[cfg(unix)]
|
||||
#[cfg(any(target_os = "linux", target_os = "macos"))]
|
||||
TransportHandle::Ethernet(t) => t.mtu(),
|
||||
TransportHandle::Tcp(t) => t.mtu(),
|
||||
TransportHandle::Tor(t) => t.mtu(),
|
||||
@@ -816,7 +816,7 @@ impl TransportHandle {
|
||||
pub fn link_mtu(&self, addr: &TransportAddr) -> u16 {
|
||||
match self {
|
||||
TransportHandle::Udp(t) => t.link_mtu(addr),
|
||||
#[cfg(unix)]
|
||||
#[cfg(any(target_os = "linux", target_os = "macos"))]
|
||||
TransportHandle::Ethernet(t) => t.link_mtu(addr),
|
||||
TransportHandle::Tcp(t) => t.link_mtu(addr),
|
||||
TransportHandle::Tor(t) => t.link_mtu(addr),
|
||||
@@ -832,7 +832,7 @@ impl TransportHandle {
|
||||
pub fn local_addr(&self) -> Option<std::net::SocketAddr> {
|
||||
match self {
|
||||
TransportHandle::Udp(t) => t.local_addr(),
|
||||
#[cfg(unix)]
|
||||
#[cfg(any(target_os = "linux", target_os = "macos"))]
|
||||
TransportHandle::Ethernet(_) => None,
|
||||
TransportHandle::Tcp(t) => t.local_addr(),
|
||||
TransportHandle::Tor(_) => None,
|
||||
@@ -848,7 +848,7 @@ impl TransportHandle {
|
||||
pub fn interface_name(&self) -> Option<&str> {
|
||||
match self {
|
||||
TransportHandle::Udp(_) => None,
|
||||
#[cfg(unix)]
|
||||
#[cfg(any(target_os = "linux", target_os = "macos"))]
|
||||
TransportHandle::Ethernet(t) => Some(t.interface_name()),
|
||||
TransportHandle::Tcp(_) => None,
|
||||
TransportHandle::Tor(_) => None,
|
||||
@@ -888,7 +888,7 @@ impl TransportHandle {
|
||||
pub fn discover(&self) -> Result<Vec<DiscoveredPeer>, TransportError> {
|
||||
match self {
|
||||
TransportHandle::Udp(t) => t.discover(),
|
||||
#[cfg(unix)]
|
||||
#[cfg(any(target_os = "linux", target_os = "macos"))]
|
||||
TransportHandle::Ethernet(t) => t.discover(),
|
||||
TransportHandle::Tcp(t) => t.discover(),
|
||||
TransportHandle::Tor(t) => t.discover(),
|
||||
@@ -904,7 +904,7 @@ impl TransportHandle {
|
||||
pub fn auto_connect(&self) -> bool {
|
||||
match self {
|
||||
TransportHandle::Udp(t) => t.auto_connect(),
|
||||
#[cfg(unix)]
|
||||
#[cfg(any(target_os = "linux", target_os = "macos"))]
|
||||
TransportHandle::Ethernet(t) => t.auto_connect(),
|
||||
TransportHandle::Tcp(t) => t.auto_connect(),
|
||||
TransportHandle::Tor(t) => t.auto_connect(),
|
||||
@@ -920,7 +920,7 @@ impl TransportHandle {
|
||||
pub fn accept_connections(&self) -> bool {
|
||||
match self {
|
||||
TransportHandle::Udp(t) => t.accept_connections(),
|
||||
#[cfg(unix)]
|
||||
#[cfg(any(target_os = "linux", target_os = "macos"))]
|
||||
TransportHandle::Ethernet(t) => t.accept_connections(),
|
||||
TransportHandle::Tcp(t) => t.accept_connections(),
|
||||
TransportHandle::Tor(t) => t.accept_connections(),
|
||||
@@ -942,7 +942,7 @@ impl TransportHandle {
|
||||
pub async fn connect(&self, addr: &TransportAddr) -> Result<(), TransportError> {
|
||||
match self {
|
||||
TransportHandle::Udp(_) => Ok(()), // connectionless
|
||||
#[cfg(unix)]
|
||||
#[cfg(any(target_os = "linux", target_os = "macos"))]
|
||||
TransportHandle::Ethernet(_) => Ok(()), // connectionless
|
||||
TransportHandle::Tcp(t) => t.connect_async(addr).await,
|
||||
TransportHandle::Tor(t) => t.connect_async(addr).await,
|
||||
@@ -962,7 +962,7 @@ impl TransportHandle {
|
||||
pub fn connection_state(&self, addr: &TransportAddr) -> ConnectionState {
|
||||
match self {
|
||||
TransportHandle::Udp(_) => ConnectionState::Connected,
|
||||
#[cfg(unix)]
|
||||
#[cfg(any(target_os = "linux", target_os = "macos"))]
|
||||
TransportHandle::Ethernet(_) => ConnectionState::Connected,
|
||||
TransportHandle::Tcp(t) => t.connection_state_sync(addr),
|
||||
TransportHandle::Tor(t) => t.connection_state_sync(addr),
|
||||
@@ -981,7 +981,7 @@ impl TransportHandle {
|
||||
pub async fn close_connection(&self, addr: &TransportAddr) {
|
||||
match self {
|
||||
TransportHandle::Udp(t) => t.close_connection(addr),
|
||||
#[cfg(unix)]
|
||||
#[cfg(any(target_os = "linux", target_os = "macos"))]
|
||||
TransportHandle::Ethernet(t) => t.close_connection(addr),
|
||||
TransportHandle::Tcp(t) => t.close_connection_async(addr).await,
|
||||
TransportHandle::Tor(t) => t.close_connection_async(addr).await,
|
||||
@@ -1006,7 +1006,7 @@ impl TransportHandle {
|
||||
pub fn congestion(&self) -> TransportCongestion {
|
||||
match self {
|
||||
TransportHandle::Udp(t) => t.congestion(),
|
||||
#[cfg(unix)]
|
||||
#[cfg(any(target_os = "linux", target_os = "macos"))]
|
||||
TransportHandle::Ethernet(_) => TransportCongestion::default(),
|
||||
TransportHandle::Tcp(_) => TransportCongestion::default(),
|
||||
TransportHandle::Tor(_) => TransportCongestion::default(),
|
||||
@@ -1026,7 +1026,7 @@ impl TransportHandle {
|
||||
TransportHandle::Udp(t) => {
|
||||
serde_json::to_value(t.stats().snapshot()).unwrap_or_default()
|
||||
}
|
||||
#[cfg(unix)]
|
||||
#[cfg(any(target_os = "linux", target_os = "macos"))]
|
||||
TransportHandle::Ethernet(t) => {
|
||||
serde_json::to_value(t.stats().snapshot()).unwrap_or_default()
|
||||
}
|
||||
|
||||
@@ -301,7 +301,7 @@ fn extract_pktinfo_ifindex(msg: &libc::msghdr) -> Option<u32> {
|
||||
if cmsg.cmsg_level == libc::IPPROTO_IPV6 && cmsg.cmsg_type == libc::IPV6_PKTINFO {
|
||||
let data_ptr = unsafe { libc::CMSG_DATA(cmsg_ptr) } as *const libc::in6_pktinfo;
|
||||
let pktinfo: libc::in6_pktinfo = unsafe { std::ptr::read_unaligned(data_ptr) };
|
||||
return Some(pktinfo.ipi6_ifindex);
|
||||
return Some(pktinfo.ipi6_ifindex as u32);
|
||||
}
|
||||
cmsg_ptr = unsafe { libc::CMSG_NXTHDR(msg, cmsg_ptr) };
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ use tracing::error;
|
||||
use tracing::{debug, trace};
|
||||
#[cfg(windows)]
|
||||
use tracing::{error, warn};
|
||||
#[cfg(unix)]
|
||||
#[cfg(any(target_os = "linux", target_os = "macos"))]
|
||||
use tun::Layer;
|
||||
|
||||
/// Read-only handle to the per-destination path MTU map. Populated by
|
||||
@@ -234,7 +234,10 @@ impl TunDevice {
|
||||
}
|
||||
}
|
||||
|
||||
// Create the TUN device
|
||||
// Create the TUN device. `mut` is only exercised on linux/macos, where
|
||||
// the name/layer/mtu are set below; other unix targets (android) pass
|
||||
// the default config through unchanged.
|
||||
#[cfg_attr(not(any(target_os = "linux", target_os = "macos")), allow(unused_mut))]
|
||||
let mut tun_config = tun::Configuration::default();
|
||||
|
||||
// On macOS, utun devices get kernel-assigned names (utun0, utun1, ...),
|
||||
@@ -1224,6 +1227,32 @@ mod windows_tun {
|
||||
#[cfg(windows)]
|
||||
pub use windows_tun::{TunDevice, TunWriter, run_tun_reader, shutdown_tun_interface};
|
||||
|
||||
// Android uses an app-owned TUN (the embedder owns the fd, e.g. an Android
|
||||
// VpnService); FIPS never creates or configures a system TUN here. These no-op
|
||||
// stubs stand in for the platform ops so the shared TunDevice code compiles.
|
||||
#[cfg(target_os = "android")]
|
||||
mod platform {
|
||||
use super::TunError;
|
||||
use std::net::Ipv6Addr;
|
||||
|
||||
pub fn is_ipv6_disabled() -> bool {
|
||||
false
|
||||
}
|
||||
pub async fn interface_exists(_name: &str) -> bool {
|
||||
false
|
||||
}
|
||||
pub async fn delete_interface(_name: &str) -> Result<(), TunError> {
|
||||
Ok(())
|
||||
}
|
||||
pub async fn configure_interface(
|
||||
_name: &str,
|
||||
_addr: Ipv6Addr,
|
||||
_mtu: u16,
|
||||
) -> Result<(), TunError> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
mod platform {
|
||||
use super::TunError;
|
||||
|
||||
Reference in New Issue
Block a user